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
@@ -0,0 +1,18 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class GuestCard < Base
6
+ string_attrs :email,
7
+ :first_name,
8
+ :guest_card_id,
9
+ :initial_source_id,
10
+ :last_name,
11
+ :property_id
12
+
13
+ date_time_attrs :create_date
14
+
15
+ alias :id :guest_card_id
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class GuestCardResult < Base
6
+ module Status
7
+ FAILURE = 'FAILURE'.freeze
8
+ SUCCESS = 'SUCCESS'.freeze
9
+ end
10
+
11
+ string_attrs *%i[
12
+ guest_card_id
13
+ property_id
14
+ status
15
+ ]
16
+
17
+ integer_attrs :contact_seq_no
18
+
19
+ alias_method :contact_sequence_number, :contact_seq_no
20
+
21
+ def success?
22
+ status == Status::SUCCESS
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,63 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class Lease < Base
6
+ module Status
7
+ CURRENT = 'C'.freeze
8
+ APPLICANT = 'A'.freeze
9
+ INTENT_TO_TRANSFER = 'I'.freeze
10
+ LEASED = 'L'.freeze
11
+ NOTICE = 'N'.freeze
12
+ PREVIOUS = 'P'.freeze
13
+ TRANSFER = 'T'.freeze
14
+ APPROVED = 'V'.freeze
15
+ CANCELLED = 'X'.freeze
16
+ end
17
+
18
+ date_attrs *%i[
19
+ application_date
20
+ lease_begin_date
21
+ lease_end_date
22
+ lease_sign_date
23
+ move_in_date
24
+ ]
25
+
26
+ string_attrs *%i[
27
+ bldg_id
28
+ external_reference_id
29
+ occu_status_code
30
+ occu_status_code_description
31
+ property_id
32
+ resi_id
33
+ unit_id
34
+ lease_marketing_source
35
+ guest_card_no
36
+ ]
37
+
38
+ decimal_attrs :rent_amount
39
+
40
+ date_time_attrs :guest_first_contact_date
41
+
42
+ alias_method :begin_date, :lease_begin_date
43
+ alias_method :end_date, :lease_end_date
44
+ alias_method :sign_date, :lease_sign_date
45
+ alias_method :lead_id, :guest_card_no
46
+ alias_method :lead_date, :guest_first_contact_date
47
+ alias_method :lead_source_code, :lease_marketing_source
48
+ alias_method :building_id, :bldg_id
49
+ alias_method :resident_id, :resi_id
50
+ alias_method :occupant_status_code, :occu_status_code
51
+ alias_method :occupant_status_code_description,
52
+ :occu_status_code_description
53
+
54
+ attr_accessor :occupants
55
+ attr_accessor :guest_card
56
+ attr_writer :matched_guest_cards
57
+
58
+ def matched_guest_cards
59
+ @matched_guest_cards ||= []
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class LeasingAgent < Base
6
+ string_attrs *%i[
7
+ agent_active_flag
8
+ agent_code
9
+ agent_name
10
+ property_id
11
+ ]
12
+
13
+ alias_method :code, :agent_code
14
+ alias_method :name, :agent_name
15
+
16
+ def active?
17
+ agent_active_flag == 'Y'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class Manager < Base
6
+ string_attrs *%i[
7
+ fax
8
+ first_name
9
+ last_name
10
+ middle_name
11
+ phone
12
+ salutation
13
+ ]
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class MarketingSource < Base
6
+ string_attrs *%i[
7
+ property_id
8
+ source_code
9
+ source_desc
10
+ source_active_flag
11
+ ]
12
+
13
+ alias_method :code, :source_code
14
+ alias_method :description, :source_desc
15
+
16
+ def active?
17
+ source_active_flag == 'Y'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class Occupant < Base
6
+ string_attrs *%i[
7
+ bldg_id
8
+ email
9
+ external_reference_id
10
+ occu_first_name
11
+ occu_last_name
12
+ occu_seq_no
13
+ phone_1_no
14
+ phone_2_no
15
+ property_id
16
+ resi_id
17
+ responsible_flag
18
+ unit_id
19
+ ]
20
+
21
+ decimal_attrs :rent_amount
22
+
23
+ alias_method :building_id, :bldg_id
24
+ alias_method :resident_id, :resi_id
25
+ alias_method :sequence_number, :occu_seq_no
26
+ alias_method :first_name, :occu_first_name
27
+ alias_method :last_name, :occu_last_name
28
+ alias_method :phone_1, :phone_1_no
29
+ alias_method :phone_2, :phone_2_no
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class Property < Base
6
+ date_attrs :live_date
7
+
8
+ string_attrs *%i[
9
+ property_id
10
+ property_name_1
11
+ property_name_2
12
+ property_addr_email
13
+ live_flag
14
+ ]
15
+
16
+ attr_accessor *%i[
17
+ address
18
+ leasing_agents
19
+ manager
20
+ marketing_sources
21
+ remit_to_address
22
+ unit_types
23
+ ]
24
+
25
+ alias_method :email, :property_addr_email
26
+ alias_method :id, :property_id
27
+ alias_method :name, :property_name_1
28
+ alias_method :name_2, :property_name_2
29
+
30
+ def live?
31
+ live_flag == 'Y'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'base'
2
+
3
+ module Amsi
4
+ module Model
5
+ class UnitType < Base
6
+ decimal_attrs *%i[
7
+ baths
8
+ market_rent
9
+ sec_min
10
+ standard_renew
11
+ mtm_renew
12
+ other_renew
13
+ ]
14
+
15
+ integer_attrs *%i[
16
+ bedrooms
17
+ rooms
18
+ sqftg
19
+ unit_type_count
20
+ ]
21
+
22
+ string_attrs *%i[
23
+ property_id
24
+ unit_subtype
25
+ unit_type
26
+ unit_type_desc
27
+ ]
28
+
29
+ alias_method :count, :unit_type_count
30
+ alias_method :description, :unit_type_desc
31
+ alias_method :minimum_security_deposit, :sec_min
32
+ alias_method :month_to_month_renew, :mtm_renew
33
+ alias_method :sqft, :sqftg
34
+ alias_method :subtype, :unit_subtype
35
+ alias_method :type, :unit_type
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,10 @@
1
+ module Amsi
2
+ module Parameter
3
+ class ContactType
4
+ INTERNET = 'I'.freeze
5
+ PHONE = 'P'.freeze
6
+ RETURN_VISIT = 'R'.freeze
7
+ VISIT = 'V'.freeze
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module Amsi
2
+ module Parameter
3
+ class Prospect
4
+ attr_reader :daytime_phone, :email, :first_name, :last_name
5
+
6
+ def initialize(
7
+ daytime_phone: nil,
8
+ email: nil,
9
+ first_name: nil,
10
+ last_name:
11
+ )
12
+ @daytime_phone = daytime_phone
13
+ @email = email
14
+ @first_name = first_name
15
+ @last_name = last_name
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,93 @@
1
+ require_relative 'base'
2
+
3
+ require 'amsi/document_parser/guest_card_result'
4
+ require 'amsi/request_section/add_guest_card'
5
+
6
+ module Amsi
7
+ module Request
8
+ # Retrieve resident leases for a given property. Returns current leases only
9
+ # by default.
10
+ #
11
+ # Required initialization parameters:
12
+ # @property_id [String] AMSI property id
13
+ # @contact_type [String] values in Amsi::Paramter::ContactType
14
+ # @prospect [Amsi::Parameter::Prospect] the prospect to create the guest
15
+ # card for
16
+ # @see request/base.rb for additional params required by every request.
17
+ class AddGuestCard < Base
18
+ def after_initialize(
19
+ appointment_date: nil,
20
+ comments: nil,
21
+ contact_datetime: nil,
22
+ contact_type:,
23
+ date_needed: nil,
24
+ initial_source_id: nil,
25
+ lease_term_desired: nil,
26
+ leasing_agent_id: nil,
27
+ property_id:,
28
+ prospect:,
29
+ qualified: nil,
30
+ requirements: nil,
31
+ unit_subtype: nil,
32
+ unit_type: nil
33
+ )
34
+ @appointment_date = appointment_date
35
+ @comments = comments
36
+ @contact_datetime = contact_datetime
37
+ @contact_type = contact_type
38
+ @date_needed = date_needed
39
+ @initial_source_id = initial_source_id
40
+ @lease_term_desired = lease_term_desired
41
+ @leasing_agent_id = leasing_agent_id
42
+ @property_id = property_id
43
+ @prospect = prospect
44
+ @qualified = qualified
45
+ @requirements = requirements
46
+ @unit_subtype = unit_subtype
47
+ @unit_type = unit_type
48
+ end
49
+
50
+ private
51
+
52
+ def sections
53
+ [
54
+ RequestSection::AddGuestCard.new(
55
+ appointment_date: appointment_date,
56
+ comments: comments,
57
+ contact_type: contact_type,
58
+ contact_datetime: contact_datetime,
59
+ date_needed: date_needed,
60
+ initial_source_id: initial_source_id,
61
+ lease_term_desired: lease_term_desired,
62
+ leasing_agent_id: leasing_agent_id,
63
+ property_id: property_id,
64
+ prospect: prospect,
65
+ qualified: qualified,
66
+ requirements: requirements,
67
+ unit_subtype: unit_subtype,
68
+ unit_type: unit_type
69
+ )
70
+ ]
71
+ end
72
+
73
+ def parser
74
+ DocumentParser::GuestCardResult.new
75
+ end
76
+
77
+ attr_reader :appointment_date,
78
+ :comments,
79
+ :contact_datetime,
80
+ :contact_type,
81
+ :date_needed,
82
+ :initial_source_id,
83
+ :lease_term_desired,
84
+ :leasing_agent_id,
85
+ :property_id,
86
+ :prospect,
87
+ :qualified,
88
+ :requirements,
89
+ :unit_subtype,
90
+ :unit_type
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,106 @@
1
+ require 'amsi/request_section/auth'
2
+ require 'amsi/utils/request_fetcher'
3
+ require 'amsi/utils/request_generator'
4
+
5
+ module Amsi
6
+ module Request
7
+ # Base class for actions that fetch and parse specific AMSI SOAP
8
+ # actions.
9
+ #
10
+ # When subclassing this base class, you may override the following methods
11
+ # to extend functionality:
12
+ #
13
+ # * after_initialize(params) - a hook into the initializer to give
14
+ # subclasses access to the parameters passed in. If the subclass uses
15
+ # any parameters that the base class is agnostic to, this is the place
16
+ # the set these values to instance variables
17
+ # * sections (returns Array<RequestSection>) - add additional request
18
+ # sections to the request XML document. The 'auth' section will always
19
+ # be inserted into the XML document by this class, so there is no need
20
+ # to add it to the response of this method.
21
+ #
22
+ # Additionally, this base class provides the #soap_action method, which
23
+ # returns the CamelCased name of the SOAP action being requested. This
24
+ # method assumes that the subclass will be named the same as the action.
25
+ class Base
26
+ # Required initialization parameters:
27
+ # @param user_id [String] AMSI account username
28
+ # @param password [String] AMSI account password
29
+ # @param portfolio_name [String] unique identifier for property in AMSI
30
+ # @param web_service_url [String] AMSI Url to the leasing.asmx resource
31
+ def initialize(
32
+ user_id:,
33
+ password:,
34
+ portfolio_name:,
35
+ web_service_url:,
36
+ conn: Faraday.new,
37
+ **other_args
38
+ )
39
+
40
+ @user_id = user_id
41
+ @password = password
42
+ @portfolio_name = portfolio_name
43
+ @web_service_url = web_service_url
44
+ @conn = conn
45
+
46
+ after_initialize(other_args)
47
+ end
48
+
49
+ # @return [Amsi::Model|Array<Amsi::Model>] the parsed response
50
+ # from AMSI
51
+ # @raise [Amsi::Error] an error when validating the response
52
+ def perform
53
+ parser.parse(xml)
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :user_id, :password, :portfolio_name, :web_service_url, :conn
59
+
60
+ # @return [String] the XMl response from Amsi
61
+ def xml
62
+ generator =
63
+ Utils::RequestGenerator.new(
64
+ sections: [auth_section, *sections],
65
+ soap_action: soap_action,
66
+ url: web_service_url
67
+ )
68
+
69
+ Utils::RequestFetcher.new(generator: generator, conn: conn).fetch
70
+ end
71
+
72
+ # A hook into the initializer to give subclasses access to the parameters
73
+ # passed in. If the subclass uses any parameters that the base class is
74
+ # agnostic to, this is the place the set these values to instance
75
+ # variables
76
+ def after_initialize(params)
77
+ # No-op, this method is a call back for subclasses
78
+ end
79
+
80
+ def parser
81
+ # No-op, this method is a call back for subclasses
82
+ end
83
+
84
+ # A hook to add additonal request sections to the request XML document.
85
+ # The 'auth' section will always be inserted into the XML document by this
86
+ # class, so there is no need to add it to the response of this method.
87
+ def sections
88
+ []
89
+ end
90
+
91
+ # The CamelCased name of the SOAP action
92
+ def soap_action
93
+ self.class.name.split('::').last
94
+ end
95
+
96
+ def auth_section
97
+ RequestSection::Auth.new(
98
+ user_id: user_id, password: password,
99
+ portfolio_name: portfolio_name
100
+ )
101
+ end
102
+ end
103
+
104
+ private_constant :Base
105
+ end
106
+ end
@@ -0,0 +1,57 @@
1
+ require_relative 'base'
2
+
3
+ require 'amsi/request_section/moveins_filter'
4
+ require 'amsi/document_parser/get_moveins'
5
+
6
+ module Amsi
7
+ module Request
8
+ # Retrieve move ins for a property between a specified data range
9
+ # and for a specified marketing source code.
10
+ #
11
+ # Required initializer parameters:
12
+ # @param property_id [String] Max of 50 characters according to their
13
+ # docs, this is AMSI's ID of the property we want to fetch move ins for.
14
+ # @param marketing_source_code [String] Max of 3 characters according to
15
+ # their docs, this is the code that corresponds to the marketing source
16
+ # for which we want to fetch move ins. The list of marketing sources
17
+ # that a given property supports can be found with a GetPropertyList
18
+ # request.
19
+ # @param from_date, through_date [Date] Together, these two params define
20
+ # the interval from which to fetch move ins.
21
+ class GetMoveinsByFirstMarketingSource < Base
22
+ def after_initialize(
23
+ from_date:,
24
+ marketing_source_code:,
25
+ property_id:,
26
+ through_date:
27
+ )
28
+ @from_date = from_date
29
+ @marketing_source_code = marketing_source_code
30
+ @property_id = property_id
31
+ @through_date = through_date
32
+ end
33
+
34
+ private
35
+
36
+ def parser
37
+ DocumentParser::GetMoveins.new
38
+ end
39
+
40
+ def sections
41
+ [
42
+ RequestSection::MoveinsFilter.new(
43
+ property_id: property_id,
44
+ marketing_source_code: marketing_source_code,
45
+ from_date: from_date,
46
+ through_date: through_date
47
+ )
48
+ ]
49
+ end
50
+
51
+ attr_reader :from_date,
52
+ :marketing_source_code,
53
+ :property_id,
54
+ :through_date
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,49 @@
1
+ require_relative 'base'
2
+
3
+ require 'amsi/request_section/property_list_filter'
4
+ require 'amsi/document_parser/properties'
5
+
6
+ module Amsi
7
+ module Request
8
+ # Retrieve resident leases for a given property. Returns current leases only
9
+ # by default.
10
+ #
11
+ # No custom required initialization parameters; @see request/base.rb for
12
+ # params required by every request.
13
+ class GetPropertyList < Base
14
+ def after_initialize(
15
+ include_leasing_agents: false,
16
+ include_marketing_sources: false,
17
+ include_unit_types: false,
18
+ property_id: nil
19
+ )
20
+ @include_leasing_agents = include_leasing_agents
21
+ @include_marketing_sources = include_marketing_sources
22
+ @include_unit_types = include_unit_types
23
+ @property_id = property_id
24
+ end
25
+
26
+ private
27
+
28
+ def parser
29
+ DocumentParser::Properties.new
30
+ end
31
+
32
+ def sections
33
+ [
34
+ RequestSection::PropertyListFilter.new(
35
+ include_leasing_agents: include_leasing_agents,
36
+ include_marketing_sources: include_marketing_sources,
37
+ include_unit_types: include_unit_types,
38
+ property_id: property_id
39
+ )
40
+ ]
41
+ end
42
+
43
+ attr_reader :include_leasing_agents,
44
+ :include_marketing_sources,
45
+ :include_unit_types,
46
+ :property_id
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'base'
2
+
3
+ require 'amsi/request_section/property_resident_filter'
4
+ require 'amsi/document_parser/leases'
5
+
6
+ module Amsi
7
+ module Request
8
+ # Retrieve resident leases for a given property. Returns current leases only
9
+ # by default.
10
+ #
11
+ # Custom required initialization parameters:
12
+ # @property_id [String] AMSI property id
13
+ # @lease_status [String] Lease status filter.
14
+ # @include_marketing_source [Boolean] flag to include marketing sources
15
+ # @params [Hash] extra configuration fields
16
+ # Valid values in: Amsi::Model::Lease::Status
17
+ # @see request/base.rb for additional params required by every request.
18
+ class GetPropertyResidents < Base
19
+ def after_initialize(property_id:, lease_status:, include_marketing_source: false, params:)
20
+ @property_id = property_id
21
+ @lease_status = lease_status
22
+ @include_marketing_source = include_marketing_source
23
+ @params = params
24
+ end
25
+
26
+ private
27
+
28
+ def parser
29
+ DocumentParser::Leases.new(@params)
30
+ end
31
+
32
+ def sections
33
+ [
34
+ RequestSection::PropertyResidentFilter.new(
35
+ property_id: property_id,
36
+ lease_status: lease_status,
37
+ include_marketing_source: include_marketing_source
38
+ )
39
+ ]
40
+ end
41
+
42
+ attr_reader :property_id, :lease_status, :include_marketing_source, :params
43
+ end
44
+ end
45
+ end