amsi 1.0.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.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.ci +125 -0
  3. data/.gitignore +2 -0
  4. data/.rspec +3 -0
  5. data/CHANGELOG.txt +20 -0
  6. data/CODEOWNERS +1 -0
  7. data/Gemfile +6 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +319 -0
  10. data/Rakefile +6 -0
  11. data/amsi.gemspec +32 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/config/multi_xml.rb +4 -0
  15. data/lib/amsi/attribute_parser/base.rb +23 -0
  16. data/lib/amsi/attribute_parser/boolean.rb +25 -0
  17. data/lib/amsi/attribute_parser/date.rb +28 -0
  18. data/lib/amsi/attribute_parser/date_time.rb +24 -0
  19. data/lib/amsi/attribute_parser/decimal.rb +15 -0
  20. data/lib/amsi/attribute_parser/integer.rb +15 -0
  21. data/lib/amsi/attribute_parser/string.rb +13 -0
  22. data/lib/amsi/attribute_parser.rb +45 -0
  23. data/lib/amsi/document_parser/base.rb +52 -0
  24. data/lib/amsi/document_parser/get_moveins.rb +66 -0
  25. data/lib/amsi/document_parser/guest_card_result.rb +31 -0
  26. data/lib/amsi/document_parser/leases.rb +46 -0
  27. data/lib/amsi/document_parser/properties.rb +103 -0
  28. data/lib/amsi/error/bad_request.rb +18 -0
  29. data/lib/amsi/error/base.rb +9 -0
  30. data/lib/amsi/error/invalid_response.rb +9 -0
  31. data/lib/amsi/error/request_fault.rb +18 -0
  32. data/lib/amsi/error/request_timeout.rb +9 -0
  33. data/lib/amsi/error/unparsable_response.rb +11 -0
  34. data/lib/amsi/model/address.rb +18 -0
  35. data/lib/amsi/model/base/attribute.rb +63 -0
  36. data/lib/amsi/model/base/attribute_store.rb +37 -0
  37. data/lib/amsi/model/base.rb +64 -0
  38. data/lib/amsi/model/guest_card.rb +18 -0
  39. data/lib/amsi/model/guest_card_result.rb +26 -0
  40. data/lib/amsi/model/lease.rb +63 -0
  41. data/lib/amsi/model/leasing_agent.rb +21 -0
  42. data/lib/amsi/model/manager.rb +16 -0
  43. data/lib/amsi/model/marketing_source.rb +21 -0
  44. data/lib/amsi/model/occupant.rb +32 -0
  45. data/lib/amsi/model/property.rb +35 -0
  46. data/lib/amsi/model/unit_type.rb +38 -0
  47. data/lib/amsi/parameter/contact_type.rb +10 -0
  48. data/lib/amsi/parameter/prospect.rb +19 -0
  49. data/lib/amsi/request/add_guest_card.rb +93 -0
  50. data/lib/amsi/request/base.rb +106 -0
  51. data/lib/amsi/request/get_moveins_by_first_marketing_source.rb +57 -0
  52. data/lib/amsi/request/get_property_list.rb +49 -0
  53. data/lib/amsi/request/get_property_residents.rb +45 -0
  54. data/lib/amsi/request_section/add_guest_card.rb +105 -0
  55. data/lib/amsi/request_section/auth.rb +22 -0
  56. data/lib/amsi/request_section/moveins_filter.rb +42 -0
  57. data/lib/amsi/request_section/property_list_filter.rb +45 -0
  58. data/lib/amsi/request_section/property_resident_filter.rb +32 -0
  59. data/lib/amsi/utils/request_fetcher.rb +37 -0
  60. data/lib/amsi/utils/request_generator.rb +54 -0
  61. data/lib/amsi/utils/snowflake_event_tracker.rb +113 -0
  62. data/lib/amsi/validator/base.rb +95 -0
  63. data/lib/amsi/validator/prospect_event_validator.rb +20 -0
  64. data/lib/amsi/validator/request_errors.rb +61 -0
  65. data/lib/amsi/validator/request_fault.rb +57 -0
  66. data/lib/amsi/validator/resident_event_validator.rb +20 -0
  67. data/lib/amsi/validator.rb +7 -0
  68. data/lib/amsi/version.rb +3 -0
  69. data/lib/amsi.rb +31 -0
  70. metadata +265 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: ba53ba480c086ec1e01d65348931bf4708372e953204b10185ac6026705a8883
4
+ data.tar.gz: '0875be24b6bc5bcf8b7d0931cf78b1746a0e042348dd8c71b112d603cff851ba'
5
+ SHA512:
6
+ metadata.gz: 328e40dde433775dc50915974d5ad85de748ffe7e5814951acf8be84a24c68776f0d6c94d9f057e0f4a4b6d004a00fbf4f577e6a509d056944fd1221047230df
7
+ data.tar.gz: 6a3dde90f9b980bf9b3277c37f7cb4ae58a0b40bf1c1e01e074252f4b64e016ac5fe1d0f74925b8c85a1b2e062ec2b7036c2506484a548211db9273a13422b20
data/.ci ADDED
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env groovy
2
+
3
+ // https://github.com/apartmentlist/ci-shared-library
4
+ @Library('ci-shared-library')_
5
+
6
+ // Log Rotation
7
+ properties([
8
+ buildDiscarder(
9
+ logRotator(
10
+ artifactDaysToKeepStr: '',
11
+ artifactNumToKeepStr: '',
12
+ daysToKeepStr: '30',
13
+ numToKeepStr: '100'
14
+ )
15
+ )
16
+ ]) //properties
17
+
18
+ // Generate unique worker labels
19
+ def k8s_label = "${UUID.randomUUID().toString()}"
20
+
21
+ pipeline {
22
+
23
+ environment {
24
+ APP_NAME = 'amsi'
25
+ APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE = "true"
26
+ CI = 'true'
27
+ CLOUDSDK_CORE_DISABLE_PROMPTS = '1'
28
+ GIT_COMMIT_SHORT = sh(script: "git rev-parse --short ${GIT_COMMIT}", returnStdout: true).trim()
29
+ GIT_MESSAGE = sh(script: "git log --format=%B -n 1 ${GIT_COMMIT}", returnStdout: true).trim()
30
+ GIT_USER = sh(script: "git log -1 --pretty=format:'%ae'", returnStdout: true).trim()
31
+ GITHUB_URL = "https://github.com"
32
+ LANG = "en_US.UTF-8"
33
+ LANGUAGE = "en_US:en"
34
+ LC_ALL = "en_US.UTF-8"
35
+ SLACK_SUCCESS_CHANNEL = "#staging-releases"
36
+ SLACK_FAILURE_CHANNEL = "#supply_log_info"
37
+ SLACK_FAILURE_MAIN = "#supply_log_error"
38
+ } // environment
39
+
40
+ agent {
41
+ kubernetes {
42
+ label k8s_label
43
+ defaultContainer 'jnlp'
44
+ yaml """
45
+ apiVersion: v1
46
+ kind: Pod
47
+ spec:
48
+ containers:
49
+ - name: ruby
50
+ image: gcr.io/alist-development/ruby:2.6.3
51
+ imagePullPolicy: Always
52
+ resources:
53
+ requests:
54
+ memory: "2048Mi"
55
+ cpu: "2"
56
+ limits:
57
+ memory: "2048Mi"
58
+ cpu: "2"
59
+ command:
60
+ - "tail"
61
+ - "-f"
62
+ - "/dev/null"
63
+ """
64
+ } // kubernetes
65
+ } // agent
66
+
67
+ options {
68
+ timestamps()
69
+ timeout(time: 30, unit: 'MINUTES')
70
+ ansiColor('xterm')
71
+ } // options
72
+
73
+ stages {
74
+
75
+ stage('Preparation') {
76
+ parallel {
77
+ stage('Slack') {
78
+ steps {
79
+ slackPreparation()
80
+ } // steps
81
+ } // stage - Slack
82
+ stage('Build Description') {
83
+ steps {
84
+ buildDescription()
85
+ } // steps
86
+ } // stage
87
+ stage('Bundle') {
88
+ steps {
89
+ container('ruby') {
90
+ sh 'gem install bundler:2.2.29'
91
+ sh 'bundle install -j 12'
92
+ } //container
93
+ } //steps
94
+ } //stage - Bundle
95
+ } //parallel
96
+ } //stage - Preparation
97
+
98
+ stage('Tests') {
99
+ parallel {
100
+ stage('Rake & Audit') {
101
+ steps {
102
+ container('ruby') {
103
+ withEnv([
104
+ "RAILS_ENV=test",
105
+ "RACK_ENV=test"
106
+ ]) {
107
+ sh 'bundle exec rspec spec'
108
+ } //withEnv
109
+ } //container
110
+ } //steps
111
+ } //stage
112
+ } //parallel
113
+ } //stage
114
+ } // stages
115
+
116
+ post {
117
+ success {
118
+ slackSuccess()
119
+ } // success
120
+ failure {
121
+ slackFailure()
122
+ } // failure
123
+ } // post
124
+
125
+ } // pipeline
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ /Gemfile.lock
2
+ amsi-*.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require 'spec_helper'
data/CHANGELOG.txt ADDED
@@ -0,0 +1,20 @@
1
+ * 1.0.0 Add eventing gem and track import_pms_resident and import_pms_prospect events for each occupant on GetPropertyResidents
2
+ * 0.12.0 Add lead_source_code, lead_date and lead_id to Model::Lease
3
+ * 0.11.5 Raise Amsi::Error::UnparsableResponse error when xml response can't be parsed
4
+ * 0.11.4 Fix ambiguous time zone bug
5
+ * 0.11.3 Add `<requirements>` node to guest card insertion request
6
+ * 0.11.2 Correctly handle gateway time outs
7
+ * 0.11.1 Parse faultstring more reliably during error
8
+ * 0.11.0 Add property_id to Model::GuestCard
9
+ * 0.10.0 Add initial_source_id to Model::GuestCard, rename guest_card_id to id
10
+ * 0.9.0 Made date interval params in GetMoveinsByFirstMarketingSource Dates
11
+ * 0.8.0 Added Model::GuestCard to response from GetMoveinsByFirstMarketingSource
12
+ * 0.7.0 Added GetMoveinsByFirstMarketingSource request
13
+ * 0.6.0 Removed SOCKS5 proxy request building; that is now up to clients
14
+ * 0.5.0 Added support for SOCKS5 proxy requests
15
+ * 0.4.0 Added GetPropertyList request
16
+ * 0.3.0 Added AddGuestCard request
17
+ * 0.2.5 Added bad request validator
18
+ * 0.2.4 Fixed Model::Occupant#occupant_status_code and added model specs
19
+ * 0.2.3 Tested by Tom locally against production data with 74 AMSI properties
20
+ * 0.1.0 Initial release with mocked GetPropertyResidents request
data/CODEOWNERS ADDED
@@ -0,0 +1 @@
1
+ * @tcollier @heidi
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in amsi.gemspec
4
+ gemspec
5
+
6
+ gem 'event_tracker', git: 'https://github.com/apartmentlist/eventing.git', glob: 'clients/ruby/event_tracker.gemspec', tag: 'rb1.35.0'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Apartment List, Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,319 @@
1
+ # Amsi
2
+
3
+ Ruby client of AMSI Evolution Suite API
4
+
5
+ ## Versions
6
+
7
+ See the [changelog](CHANGELOG.txt)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'amsi'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install amsi
24
+
25
+ ## Usage
26
+
27
+ ### SOAP Actions
28
+
29
+ To request an action, instantiate the appropriate request class (see below) and invoke #perform
30
+ on it. The API response will be parsed and returned as a ruby object.
31
+
32
+ #### Amsi::Request::AddGuestCard
33
+ Add a new guest card for the prospect
34
+
35
+ ##### Initialization parameters
36
+
37
+ Required initializer parameters:
38
+ * `contact_type` [String] See [Amsi::Parameter::ContactType](#amsiparametercontacttype)
39
+ * `password` [String] Amsi account password
40
+ * `portfolio_name` [String] the unique identifier for the property in AMSI
41
+ * `property_id` [String] AMSI property id
42
+ * `prospect` [Amsi::Parameter::Prospect](#amsiparameterprospect)
43
+ * `user_id` [String] Amsi account username
44
+ * `web_service_url` [String] Amsi Url to the leasing.asmx resource
45
+
46
+ Optional initializer parameters:
47
+ * `appointment_date` [Date] date the prospect is going to visit the property
48
+ * `comments` [String]
49
+ * `contact_datetime` [DateTime]
50
+ * `date_needed` [Date]
51
+ * `initial_source_id` [String]
52
+ * `lease_term_desired` [Integer]
53
+ * `leasing_agent_id` [String]
54
+ * `qualified` [Boolean]
55
+ * `unit_subtype` [String]
56
+ * `unit_type` [String]
57
+
58
+ ##### Response
59
+
60
+ [Amsi::Model::GuestCardResult](#amsimodelguestcardresult)
61
+
62
+ #### Amsi::Request::GetMoveinsByFirstMarketingSource
63
+ Get all leases matching the specified marketing source and within the specified
64
+ time window.
65
+
66
+ ##### Initialization parameters
67
+
68
+ Required initializer parameters:
69
+ * `from_date` [Date] Beginning of date interval within which to search
70
+ * `marketing_source_code` [String] Code corresponding to the marketing source
71
+ for which to fetch move ins. Max of 3 characters according to their docs.
72
+ * `password` [String] Amsi account password
73
+ * `portfolio_name` [String] the unique identifier for the property in AMSI
74
+ * `property_id` [String] AMSI property id
75
+ * `through_date` [Date] End of date interval within which to search
76
+ * `user_id` [String] Amsi account username
77
+ * `web_service_url` [String] Amsi Url to the leasing.asmx resource
78
+
79
+ ##### Response
80
+
81
+ Array<[Amsi::Model::Lease](#amsimodellease)>
82
+
83
+ #### Amsi::Request::GetPropertyList
84
+ Retrieve a list of properties and detailed information about each.
85
+
86
+ ##### Initialization parameters
87
+
88
+ Required initializer parameters:
89
+ * `user_id` [String] Amsi account username
90
+ * `password` [String] Amsi account password
91
+ * `portfolio_name` [String] the unique identifier for the property in AMSI
92
+ * `web_service_url` [String] Amsi Url to the leasing.asmx resource
93
+
94
+ Optional initializer parameters and default values:
95
+ * `property_id` [String] Amsi property id (if this field is left blank, the response will
96
+ include all properties in the portfolio)
97
+ * `include_leasing_agents` [Boolean] if true, response will include leasing agents for each property returned.
98
+ * `include_marketing_sources` [Boolean] if true, response will include marketing sources for each property returned.
99
+ * `include_unit_types` [Boolean] if true, response will include unit types for each property returned.
100
+
101
+
102
+ ##### Response
103
+
104
+ Array<[Amsi::Model::Property](#amsimodelproperty)>
105
+
106
+
107
+ #### Amsi::Request::GetPropertyResidents
108
+ Retrieve resident leases for a given property. Returns current leases only by default.
109
+
110
+ ##### Initialization parameters
111
+
112
+ Required initializer parameters:
113
+ * `user_id` [String] Amsi account username
114
+ * `password` [String] Amsi account password
115
+ * `portfolio_name` [String] the unique identifier for the property in Amsi
116
+ * `property_id` [String] Amsi property id
117
+ * `web_service_url` [String] Amsi Url to the leasing.asmx resource
118
+ * `params` [Hash] extra configuration fields: `:start_date` [String], `:end_date` [String], `:remote_id` [String], `:billing_config` [BillingConfiguration], `:import_id` [String] and `:pmc_id` [String]
119
+
120
+ Optional initializer parameters and default values:
121
+ * `lease_status` [String] Lease status filter; defaults to current leases ("C"). Valid values: (A)pplicant, (C)urrent, (I)ntent to Transfer, (L)eased, (N)otice, (P)revious, (T)ransfer, (V)approved, (X)cancelled
122
+ * `include_marketing_source` [Boolean] if true, response will include marketing sources on each lease returned.
123
+
124
+ ##### Response
125
+
126
+ Array<[Amsi::Model::Lease](#amsimodelfloorplan)>
127
+
128
+
129
+ ### Parameters
130
+
131
+ #### Amsi::Parameter::ContactType
132
+
133
+ Constants for the different channels prospects can use for contacting. Available constants are:
134
+
135
+ * `INTERNET`
136
+ * `PHONE`
137
+ * `RETURN_VISIT`
138
+ * `VISIT`
139
+
140
+ #### Amsi::Parameter::Prospect
141
+
142
+ A container for prospect details.
143
+
144
+ Required initializer parameters:
145
+ * `last_name` [String]
146
+
147
+ Optional initializer parameters:
148
+ * `daytime_phone` [String]
149
+ * `email` [String]
150
+ * `first_name` [String]
151
+
152
+
153
+ ### Models
154
+
155
+ #### Amsi::Model::Address
156
+
157
+ A [Amsi::Model::Property](#amsimodelproperty) has an address and a remit_to_address
158
+
159
+ * `line_1` [String] 1st line in the street address
160
+ * `line_2` [String] 2nd line in the street address
161
+ * `line_3` [String] 3rd line in the street address
162
+ * `line_4` [String] 4th line in the street address
163
+ * `city` [String] the city
164
+ * `state` [String] the state
165
+ * `zip_code` [String] the zip code
166
+ * `country` [String] the country
167
+
168
+ #### Amsi::Model::GuestCardResult
169
+
170
+ The response from [AddGuestCard](#amsirequestaddguestcard), will have the following attributes:
171
+
172
+ * `contact_sequence_number` [Integer] (alias `contact_seq_no`)
173
+ * `guest_card_id` [String] the unique identifier for the newly created guest card
174
+ * `property_id` [String]
175
+ * `status` [String] either "SUCCESS" or "FAILURE" (also has convenience method `success?`)
176
+
177
+ #### Amsi::Model::GuestCard
178
+
179
+ The response from [GetMoveinsByFirstMarketingSource](#amsirequestsgetmoveinsbyfirstmarketingsource)
180
+ is an array of `Amsi::Model::Lease` instances. Leases have `guest_card` and
181
+ `matched_guest_cards`. `guest_card` is either an Amsi::Model::GuestCard or nil
182
+ and `matched_guest_cards` is an array of Amsi::Model::GuestCards
183
+ (can be empty). Amsi::Model::GuestCards have the following attributes:
184
+
185
+ * `email` [String]
186
+ * `create_date` [DateTime] The time the guest card was created according to AMSI
187
+ * `first_name` [String] The renter's first name
188
+ * `id` [String] The ID of the guest card in AMSI
189
+ * `initial_source_id` [String] the short code for the marketing source attributed to the lease
190
+ * `last_name` [String] The renter's last name
191
+ * `property_id` [String] The unique identifier of the property that the guest card belongs to
192
+
193
+ #### Amsi::Model::Lease
194
+
195
+ The response from [GetPropertyResidents](#amsirequestgetpropertyresidents) is an array of `Amsi::Model::Lease`
196
+ instances. Each instance will have the following attributes:
197
+
198
+ * `property_id` [String]
199
+ * `building_id` [String] Building ID
200
+ * `unit_id` [String] Unit name
201
+ * `resident_id` [String] Resident ID
202
+ * `external_reference_id` [String]
203
+ * `guest_card` [[Amsi::Model::GuestCard](#amsimodelguestcard)] or nil
204
+ * `matched_guest_cards` [Array<[Amsi::Model::GuestCard](#amsimodelguestcard)>], can be []
205
+ * `occupant_status_code` [String in Amsi::Model::Lease::Status]
206
+ * `occupant_status_code_description` [String]
207
+ * `begin_date` [Date]
208
+ * `end_date` [Date]
209
+ * `application_date` [Date]
210
+ * `sign_date` [Date]
211
+ * `move_in_date` [Date]
212
+ * `rent_amount` [Integer]
213
+ * `occupants` [Array<[Amsi::Model::Occupant](#amsimodeloccupant)>]
214
+ * `lead_id` [String] Guest Card No.
215
+ * `lead_date` [DateTime] Guest first contact date
216
+ * `lead_source_code` [String] Marketing source code
217
+
218
+ #### Amsi::Model::LeasingAgent
219
+
220
+ A [Amsi::Model::Property](#amsimodelproperty) has 0 or more leasing agents
221
+
222
+ * `agent_active_flag` [String] "Y" if the agent is active, otherwise "N" (also has convenience method `active?`)
223
+ * `code` [String] the unique identifier for the agent within the property (alias of `agent_code`)
224
+ * `agent_name` [String] the leasing agent's full name (alias of `agent_name`)
225
+ * `property_id` [String] the unique ID of the property the leasing agent works at
226
+
227
+ #### Amsi::Model::Manager
228
+
229
+ A [Amsi::Model::Property](#amsimodelproperty) has a manager
230
+
231
+ * `fax` [String] the property manager's fax number
232
+ * `first_name` [String] the property manager's first name
233
+ * `last_name` [String] the property manager's last name
234
+ * `middle_name` [String] the property manager's middle name
235
+ * `phone` [String] the property manager's phone number
236
+ * `salutation` [String] the property manager's salutation, e.g. "Mrs"
237
+
238
+ #### Amsi::Model::MarketingSource
239
+
240
+ A [Amsi::Model::Property](#amsimodelproperty) has 0 or more marketing sources
241
+
242
+ * `code` [String] unique identifier within the property (alias of `source_code`)
243
+ * `description` [String] a description of the marketing source (alias of `source_desc`)
244
+ * `source_active_flag` [String] "Y" if the marketing source is active, otherwise "N" (also has convenience method `active?`)
245
+ * `property_id` [String] the unique ID of the property the marketing source belongs to
246
+
247
+ #### Amsi::Model::Occupant
248
+
249
+ One occupant of a lease. Each instance will have the following attributes:
250
+
251
+ * `property_id` [String]
252
+ * `building_id` [String] Building ID
253
+ * `unit_id` [String] Unit name
254
+ * `resident_id` [String] Resident ID
255
+ * `sequence_number` [String] Sequence number
256
+ * `first_name` [String]
257
+ * `last_name` [String]
258
+ * `phone_1` [String]
259
+ * `phone_2` [String]
260
+ * `responsible_flag` [String in ["Responsible" | "Non-Responsible"]]
261
+ * `email` [String]
262
+
263
+ #### Amsi::Model::Property
264
+
265
+ The response from [GetPropertyList](#amsirequestgetpropertylist) is an array of `Amsi::Model::Property`
266
+ instances. Each instance will have the following attributes:
267
+
268
+ * `address` [[Amsi::Model::Address](#amsimodeladdress)] the property's physical address
269
+ * `email` [String] the email address of the property (alias of `property_addr_email`)
270
+ * `id` [String] the property's unique identifier
271
+ * `leasing_agents` [Array<[Amsi::Model::LeasingAgent](#amsimodelleasingagent)>] the list of leasing agents for the property
272
+ * `live_date` [Date] the date the property went live
273
+ * `live_flag` [String] "Y" if the property is live, otherwise "N" (also has convenience method `live?`)
274
+ * `manager` [[Amsi::Model::Manager](#amsimodelmanager)] the property manager's contact info
275
+ * `marketing_sources` [Array<[Amsi::Model::MarketingSource](#amsimodelmarketingsource)>] the list of marketing sources for the property
276
+ * `name` [String] the name of the property (alias of `property_name_1`)
277
+ * `name_2` [String] an optional alternate name of the property (alias of `property_name_2`)
278
+ * `remit_to_address` [[Amsi::Model::Address](#amsimodeladdress)] the property's mailing address
279
+ * `unit_types` [Array<[Amsi::Model::UnitType](#amsimodelunittype)>] the list of unit types for the property
280
+
281
+ #### Amsi::Model::UnitType
282
+
283
+ A [Amsi::Model::Property](#amsimodelproperty) has 0 or more unit types
284
+
285
+ * `baths` [Decimal] the number of bathrooms in the unit
286
+ * `bedrooms` [Integer] the number of bedrooms in the unit
287
+ * `count` [Integer] Number of units in the property that are of this type
288
+ * `description` [String] a description of the unit type (alias of `unit_type_desc`)
289
+ * `market_rent` [Decimal] the rental price of the unit
290
+ * `minimum_security_deposit` [Decimal] the minimum security deposit needed for the unit (alias of `sec_min`)
291
+ * `month_to_month_renew` [Decimal] the month-to-month renewal amount (alias of `mtm_renew`)
292
+ * `other_renew` [Decimal] other renewal amount
293
+ * `property_id` [String] the unique ID of the property the unit is in
294
+ * `rooms` [Integer] the total number of rooms in the unit
295
+ * `sqft` [Integer] the total area of the unit (alias of `sqftg`)
296
+ * `standard_renew` [Decimal] the standard renewal amount
297
+ * `type` [String] the unique identifier within the property for this unit type (alias of `unit_type`)
298
+
299
+
300
+ ## Compatibility
301
+
302
+ This gem sets global configurations on other dependent libraries, which makes it inherently *not*
303
+ thread-safe. Notably, it sets the following
304
+
305
+ * `MultiXml.parser = :ox`
306
+
307
+ If your client depends on any of these global settings, it is recommended that you do not run in
308
+ a multi-threaded environment.
309
+
310
+
311
+ ## Development
312
+
313
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
314
+
315
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
316
+
317
+ ## Contributing
318
+
319
+ Bug reports and pull requests are welcome on GitHub at https://github.com/apartmentlist/amsi.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/amsi.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'amsi/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'amsi'
8
+ spec.version = Amsi::VERSION
9
+ spec.authors = ['Justin Richard']
10
+ spec.email = ['justin@apartmentlist.com']
11
+
12
+ spec.summary = 'Ruby client of AMSI Evolution Suite API'
13
+ spec.homepage = 'https://github.com/apartmentlist/amsi'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
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'
22
+ spec.add_runtime_dependency 'nokogiri', '~> 1.6'
23
+ spec.add_runtime_dependency 'ox', '~> 2.3'
24
+ spec.add_runtime_dependency 'tzinfo', '~> 1.2'
25
+
26
+ spec.add_development_dependency 'bundler', '>= 2'
27
+ spec.add_development_dependency 'pry-byebug', '~> 3.3'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec', '~> 3.4'
30
+ spec.add_development_dependency 'timecop', '~> 0.8'
31
+ spec.add_development_dependency 'webmock', '~> 1.21'
32
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'pry'
5
+ require 'amsi'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require 'pry'
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ require 'multi_xml'
2
+ require 'ox'
3
+
4
+ MultiXml.parser = :ox
@@ -0,0 +1,23 @@
1
+ require 'amsi/error/invalid_response'
2
+
3
+ module Amsi
4
+ class AttributeParser
5
+ # Base class for attribute parsers.
6
+ class Base
7
+ # @param value [String] the response value from AMSI
8
+ def initialize(value)
9
+ @value = value
10
+ end
11
+
12
+ # @return [Object] the parsed attribute value
13
+ def parse
14
+ raise NotImplementedError,
15
+ "#{self.class.name} must implement #{__method__}"
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :value
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ class AttributeParser
5
+ # Parse the response value of a boolean attribute
6
+ class Boolean < Base
7
+ # Values that AMSI responds with for true
8
+ TRUE_VALUES = %w[1 true].freeze
9
+ private_constant :TRUE_VALUES
10
+
11
+ # Values that AMSI responds with for true
12
+ FALSE_VALUES = %w[0 false].freeze
13
+ private_constant :FALSE_VALUES
14
+
15
+ # @return [true|false] the parsed attribute value
16
+ # @raise [Amsi::Error::InvalidResponse] if the value doesn't parse
17
+ # into true or false
18
+ def parse
19
+ return true if TRUE_VALUES.include?(value)
20
+ return false if FALSE_VALUES.include?(value)
21
+ raise Error::InvalidResponse, "Invalid boolean response value: #{value}"
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ class AttributeParser
5
+ # Parse the response value of a date attribute
6
+ class Date < Base
7
+ # AMSI responds with multiple date formats. This is one format that
8
+ # Date.parse will not parse correctly, so we need special handling
9
+ FORMAT = '%m/%d/%Y'.freeze
10
+ private_constant :FORMAT
11
+
12
+ # @return [Date] the parsed attribute value
13
+ def parse
14
+ return if value == ''
15
+ if value =~ %r[/]
16
+ ::Date.strptime(value, FORMAT)
17
+ else
18
+ # AMSI sometimes returns 0001-01-01 for dates, which appears to be
19
+ # their representation of a NULL value.
20
+ date = ::Date.parse(value)
21
+ date.year == 1 ? nil : date
22
+ end
23
+ rescue ArgumentError
24
+ raise Error::InvalidResponse, "Invalid date response value: #{value}"
25
+ end
26
+ end
27
+ end
28
+ end