amsi 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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