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.
- checksums.yaml +7 -0
- data/.ci +125 -0
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/CHANGELOG.txt +20 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +319 -0
- data/Rakefile +6 -0
- data/amsi.gemspec +32 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/config/multi_xml.rb +4 -0
- data/lib/amsi/attribute_parser/base.rb +23 -0
- data/lib/amsi/attribute_parser/boolean.rb +25 -0
- data/lib/amsi/attribute_parser/date.rb +28 -0
- data/lib/amsi/attribute_parser/date_time.rb +24 -0
- data/lib/amsi/attribute_parser/decimal.rb +15 -0
- data/lib/amsi/attribute_parser/integer.rb +15 -0
- data/lib/amsi/attribute_parser/string.rb +13 -0
- data/lib/amsi/attribute_parser.rb +45 -0
- data/lib/amsi/document_parser/base.rb +52 -0
- data/lib/amsi/document_parser/get_moveins.rb +66 -0
- data/lib/amsi/document_parser/guest_card_result.rb +31 -0
- data/lib/amsi/document_parser/leases.rb +46 -0
- data/lib/amsi/document_parser/properties.rb +103 -0
- data/lib/amsi/error/bad_request.rb +18 -0
- data/lib/amsi/error/base.rb +9 -0
- data/lib/amsi/error/invalid_response.rb +9 -0
- data/lib/amsi/error/request_fault.rb +18 -0
- data/lib/amsi/error/request_timeout.rb +9 -0
- data/lib/amsi/error/unparsable_response.rb +11 -0
- data/lib/amsi/model/address.rb +18 -0
- data/lib/amsi/model/base/attribute.rb +63 -0
- data/lib/amsi/model/base/attribute_store.rb +37 -0
- data/lib/amsi/model/base.rb +64 -0
- data/lib/amsi/model/guest_card.rb +18 -0
- data/lib/amsi/model/guest_card_result.rb +26 -0
- data/lib/amsi/model/lease.rb +63 -0
- data/lib/amsi/model/leasing_agent.rb +21 -0
- data/lib/amsi/model/manager.rb +16 -0
- data/lib/amsi/model/marketing_source.rb +21 -0
- data/lib/amsi/model/occupant.rb +32 -0
- data/lib/amsi/model/property.rb +35 -0
- data/lib/amsi/model/unit_type.rb +38 -0
- data/lib/amsi/parameter/contact_type.rb +10 -0
- data/lib/amsi/parameter/prospect.rb +19 -0
- data/lib/amsi/request/add_guest_card.rb +93 -0
- data/lib/amsi/request/base.rb +106 -0
- data/lib/amsi/request/get_moveins_by_first_marketing_source.rb +57 -0
- data/lib/amsi/request/get_property_list.rb +49 -0
- data/lib/amsi/request/get_property_residents.rb +45 -0
- data/lib/amsi/request_section/add_guest_card.rb +105 -0
- data/lib/amsi/request_section/auth.rb +22 -0
- data/lib/amsi/request_section/moveins_filter.rb +42 -0
- data/lib/amsi/request_section/property_list_filter.rb +45 -0
- data/lib/amsi/request_section/property_resident_filter.rb +32 -0
- data/lib/amsi/utils/request_fetcher.rb +37 -0
- data/lib/amsi/utils/request_generator.rb +54 -0
- data/lib/amsi/utils/snowflake_event_tracker.rb +113 -0
- data/lib/amsi/validator/base.rb +95 -0
- data/lib/amsi/validator/prospect_event_validator.rb +20 -0
- data/lib/amsi/validator/request_errors.rb +61 -0
- data/lib/amsi/validator/request_fault.rb +57 -0
- data/lib/amsi/validator/resident_event_validator.rb +20 -0
- data/lib/amsi/validator.rb +7 -0
- data/lib/amsi/version.rb +3 -0
- data/lib/amsi.rb +31 -0
- metadata +265 -0
@@ -0,0 +1,105 @@
|
|
1
|
+
module Amsi
|
2
|
+
module RequestSection
|
3
|
+
# Generate the AddGuestCard elements of an Amsi request
|
4
|
+
class AddGuestCard
|
5
|
+
# Formats dates and times like "mm/dd/yyyy", "02/14/2015"
|
6
|
+
DATE_FORMAT = '%m/%d/%Y'
|
7
|
+
private_constant :DATE_FORMAT
|
8
|
+
|
9
|
+
def initialize(
|
10
|
+
appointment_date: nil,
|
11
|
+
comments: nil,
|
12
|
+
contact_datetime: nil,
|
13
|
+
contact_type:,
|
14
|
+
date_needed: nil,
|
15
|
+
initial_source_id: nil,
|
16
|
+
lease_term_desired: nil,
|
17
|
+
leasing_agent_id: nil,
|
18
|
+
property_id:,
|
19
|
+
prospect:,
|
20
|
+
requirements: nil,
|
21
|
+
qualified: nil,
|
22
|
+
unit_subtype: nil,
|
23
|
+
unit_type: nil
|
24
|
+
)
|
25
|
+
@appointment_date = appointment_date
|
26
|
+
@comments = comments
|
27
|
+
@contact_datetime = contact_datetime
|
28
|
+
@contact_type = contact_type
|
29
|
+
@date_needed = date_needed
|
30
|
+
@initial_source_id = initial_source_id
|
31
|
+
@lease_term_desired = lease_term_desired
|
32
|
+
@leasing_agent_id = leasing_agent_id
|
33
|
+
@property_id = property_id
|
34
|
+
@prospect = prospect
|
35
|
+
@requirements = requirements
|
36
|
+
@qualified = qualified
|
37
|
+
@unit_subtype = unit_subtype
|
38
|
+
@unit_type = unit_type
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate(xml_builder)
|
42
|
+
xml_builder.XMLData do |xml_data|
|
43
|
+
xml_data.cdata edex.to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_reader :appointment_date,
|
50
|
+
:comments,
|
51
|
+
:contact_datetime,
|
52
|
+
:contact_type,
|
53
|
+
:date_needed,
|
54
|
+
:initial_source_id,
|
55
|
+
:lease_term_desired,
|
56
|
+
:leasing_agent_id,
|
57
|
+
:property_id,
|
58
|
+
:prospect,
|
59
|
+
:requirements,
|
60
|
+
:qualified,
|
61
|
+
:unit_subtype,
|
62
|
+
:unit_type
|
63
|
+
|
64
|
+
def edex
|
65
|
+
Nokogiri::XML::Builder.new do |xml_builder|
|
66
|
+
xml_builder.EDEX do |edex|
|
67
|
+
edex.propertyid(property_id)
|
68
|
+
edex.contacttype(contact_type)
|
69
|
+
edex.contactdatetime(contact_time) if contact_datetime
|
70
|
+
edex.modifieddate(format_date(Date.today))
|
71
|
+
edex.initialsourceid(initial_source_id) if initial_source_id
|
72
|
+
edex.qualified(qualified ? 'Y' : 'N') unless qualified.nil?
|
73
|
+
edex.leasetermdesired(lease_term_desired) if lease_term_desired
|
74
|
+
edex.firstname(prospect.first_name) if prospect.first_name
|
75
|
+
edex.lastname(prospect.last_name)
|
76
|
+
edex.dateneeded(format_date(date_needed)) if date_needed
|
77
|
+
edex.daytimephone(prospect.daytime_phone) if prospect.daytime_phone
|
78
|
+
edex.email(prospect.email) if prospect.email
|
79
|
+
edex.appointmentdate(appointment) if appointment_date
|
80
|
+
edex.unittype(unit_type) if unit_type
|
81
|
+
edex.unitsubtype(unit_subtype) if unit_subtype
|
82
|
+
edex.leasingagentid(leasing_agent_id) if leasing_agent_id
|
83
|
+
edex.requirements(requirements) if requirements
|
84
|
+
edex.comments do |comments_xml|
|
85
|
+
comments_xml.comment_(comments, date: format_date(Date.today))
|
86
|
+
end if comments
|
87
|
+
end
|
88
|
+
end.doc.root
|
89
|
+
end
|
90
|
+
|
91
|
+
def contact_time
|
92
|
+
contact_datetime.strftime('%m/%d/%Y %0l:%M %p')
|
93
|
+
end
|
94
|
+
|
95
|
+
def appointment
|
96
|
+
format_date(appointment_date)
|
97
|
+
end
|
98
|
+
|
99
|
+
def format_date(date)
|
100
|
+
# Formats dates and times like "mm/dd/yyyy", "02/14/2015"
|
101
|
+
date.strftime(DATE_FORMAT)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Amsi
|
2
|
+
module RequestSection
|
3
|
+
# Generate the auth elements of an AMSI request
|
4
|
+
class Auth
|
5
|
+
def initialize(user_id:, password:, portfolio_name:)
|
6
|
+
@user_id = user_id
|
7
|
+
@password = password
|
8
|
+
@portfolio_name = portfolio_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate(xml_builder)
|
12
|
+
xml_builder.UserID user_id
|
13
|
+
xml_builder.Password password
|
14
|
+
xml_builder.PortfolioName portfolio_name
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :user_id, :password, :portfolio_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Amsi
|
2
|
+
module RequestSection
|
3
|
+
# Generate the move in filter elements of an Amsi request
|
4
|
+
class MoveinsFilter
|
5
|
+
def initialize(
|
6
|
+
property_id:,
|
7
|
+
marketing_source_code:,
|
8
|
+
from_date:,
|
9
|
+
through_date:
|
10
|
+
)
|
11
|
+
@property_id = property_id
|
12
|
+
@marketing_source_code = marketing_source_code
|
13
|
+
@from_date = from_date
|
14
|
+
@through_date = through_date
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate(xml_builder)
|
18
|
+
xml_builder.XMLData do |xml_data|
|
19
|
+
xml_data.cdata edex.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def edex
|
26
|
+
Nokogiri::XML::Builder.new do |xml_builder|
|
27
|
+
xml_builder.EDEX do |edex|
|
28
|
+
edex.propertyid(property_id)
|
29
|
+
edex.marketingsourcecode(marketing_source_code)
|
30
|
+
edex.fromdate(from_date.to_s)
|
31
|
+
edex.thrudate(through_date.to_s)
|
32
|
+
end
|
33
|
+
end.doc.root
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :from_date,
|
37
|
+
:marketing_source_code,
|
38
|
+
:property_id,
|
39
|
+
:through_date
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Amsi
|
2
|
+
module RequestSection
|
3
|
+
# Generate the property list filter elements of an AMSI request
|
4
|
+
class PropertyListFilter
|
5
|
+
def initialize(
|
6
|
+
include_leasing_agents: false,
|
7
|
+
include_marketing_sources: false,
|
8
|
+
include_unit_types: false,
|
9
|
+
property_id: nil
|
10
|
+
)
|
11
|
+
@include_leasing_agents = include_leasing_agents
|
12
|
+
@include_marketing_sources = include_marketing_sources
|
13
|
+
@include_unit_types = include_unit_types
|
14
|
+
@property_id = property_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate(xml_builder)
|
18
|
+
xml_builder.XMLData do |xml_data|
|
19
|
+
xml_data.cdata edex.to_s
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def edex
|
26
|
+
Nokogiri::XML::Builder.new do |xml_builder|
|
27
|
+
xml_builder.EDEX do |edex|
|
28
|
+
edex.propertyid(property_id)
|
29
|
+
edex.includeamenities('0')
|
30
|
+
edex.includeclasses('0')
|
31
|
+
edex.includeincomecodes('0')
|
32
|
+
edex.includeleasingagents(include_leasing_agents ? '1' : '0')
|
33
|
+
edex.includemarketingsources(include_marketing_sources ? '1' : '0')
|
34
|
+
edex.includeunittypes(include_unit_types ? '1' : '0')
|
35
|
+
end
|
36
|
+
end.doc.root
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_reader :include_leasing_agents,
|
40
|
+
:include_marketing_sources,
|
41
|
+
:include_unit_types,
|
42
|
+
:property_id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Amsi
|
2
|
+
module RequestSection
|
3
|
+
# Generate the property resident filter elements of an AMSI request
|
4
|
+
class PropertyResidentFilter
|
5
|
+
def initialize(property_id:, lease_status:, include_marketing_source: false)
|
6
|
+
@property_id = property_id
|
7
|
+
@lease_status = lease_status
|
8
|
+
@include_marketing_source = include_marketing_source
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate(xml_builder)
|
12
|
+
xml_builder.XMLData do |xml_data|
|
13
|
+
xml_data.cdata edex.to_s
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def edex
|
20
|
+
Nokogiri::XML::Builder.new do |xml_builder|
|
21
|
+
xml_builder.EDEX do |edex|
|
22
|
+
edex.propertyid property_id
|
23
|
+
edex.leasestatus lease_status
|
24
|
+
edex.includemarketingsource include_marketing_source ? '1' : '0'
|
25
|
+
end
|
26
|
+
end.doc.root
|
27
|
+
end
|
28
|
+
|
29
|
+
attr_reader :property_id, :lease_status, :include_marketing_source
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
module Amsi
|
4
|
+
module Utils
|
5
|
+
# Send a SOAP request to AMSI
|
6
|
+
class RequestFetcher
|
7
|
+
module Status
|
8
|
+
TIMEOUT = 504
|
9
|
+
end
|
10
|
+
|
11
|
+
# @param generator [RequestGenerator] an instance of a RequestGenerator
|
12
|
+
def initialize(generator:, conn: Faraday.new)
|
13
|
+
@generator = generator
|
14
|
+
@conn = conn
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [String] the XML response from AMSI
|
18
|
+
def fetch
|
19
|
+
response = conn.post(generator.url) do |request|
|
20
|
+
request.body = generator.body
|
21
|
+
request.headers = generator.headers
|
22
|
+
end
|
23
|
+
|
24
|
+
if response.status == Status::TIMEOUT
|
25
|
+
raise Error::RequestTimeout,
|
26
|
+
'The server did not respond in a reasonable timeframe'
|
27
|
+
end
|
28
|
+
|
29
|
+
response.body
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :generator, :conn
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
|
3
|
+
module Amsi
|
4
|
+
module Utils
|
5
|
+
# Generate a SOAP request for a specific action and sections
|
6
|
+
class RequestGenerator
|
7
|
+
ENVELOPE_ATTRIBUTES = {
|
8
|
+
'xmlns:soap' => 'http://schemas.xmlsoap.org/soap/envelope/'
|
9
|
+
}.freeze
|
10
|
+
|
11
|
+
ACTION_ATTRIBUTES = {
|
12
|
+
'xmlns' => 'http://tempuri.org/'
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
# @param url: [String] the URL to POST to
|
16
|
+
# @param soap_action: [String] the PascalCase action to request from AMSI.
|
17
|
+
# @param sections: [Array<RequestSection>] the setion generators that will
|
18
|
+
# be used to generate the body of the XML request
|
19
|
+
def initialize(url:, soap_action:, sections:)
|
20
|
+
@url = url
|
21
|
+
@soap_action = soap_action
|
22
|
+
@sections = sections
|
23
|
+
end
|
24
|
+
|
25
|
+
def headers
|
26
|
+
{
|
27
|
+
'Content-Type' => 'text/xml; charset=utf-8',
|
28
|
+
'SOAPAction' => "http://tempuri.org/#{soap_action}"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] the XML request for the specified action and sections
|
33
|
+
def body
|
34
|
+
Nokogiri::XML::Builder.new do |xml_builder|
|
35
|
+
xml_builder['soap'].Envelope(ENVELOPE_ATTRIBUTES) do
|
36
|
+
xml_builder['soap'].Body do
|
37
|
+
xml_builder.send(soap_action, ACTION_ATTRIBUTES) do
|
38
|
+
sections.each do |section|
|
39
|
+
section.generate(xml_builder)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end.to_xml
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :url
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :soap_action, :sections
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'event_tracker'
|
2
|
+
|
3
|
+
module Amsi
|
4
|
+
module Utils
|
5
|
+
class SnowflakeEventTracker
|
6
|
+
IMPORT_PMS_RESIDENT_EVENT = 'import_pms_resident'
|
7
|
+
IMPORT_PMS_PROSPECT_EVENT = 'import_pms_prospect'
|
8
|
+
|
9
|
+
def self.track_pms_resident_event(
|
10
|
+
remote_lease_id: nil,
|
11
|
+
import_resident_id:,
|
12
|
+
resident_type:,
|
13
|
+
request_params:,
|
14
|
+
move_in_date: nil,
|
15
|
+
lease_to: nil,
|
16
|
+
lease_from: nil,
|
17
|
+
first_name_present: false,
|
18
|
+
last_name_present: false,
|
19
|
+
email_present: false,
|
20
|
+
phones_count: 0,
|
21
|
+
unit_name:,
|
22
|
+
resident_id: nil,
|
23
|
+
error: nil
|
24
|
+
)
|
25
|
+
EventTracker.track_process_events(name: IMPORT_PMS_RESIDENT_EVENT) do |events|
|
26
|
+
events.add_imported_event(
|
27
|
+
EventTracker::ResourceFactory.build_pms_resident(
|
28
|
+
billing_import: EventTracker::BillingImportFactory.build_billing_import(
|
29
|
+
property_id: request_params[:property_id],
|
30
|
+
billing_config_id: request_params[:billing_config_id],
|
31
|
+
remote_id: request_params[:remote_id],
|
32
|
+
pms_type: 'amsi',
|
33
|
+
import_id: request_params[:import_id],
|
34
|
+
pmc_id: request_params[:pmc_id],
|
35
|
+
service: 'amsi'
|
36
|
+
),
|
37
|
+
remote_lease_id: remote_lease_id,
|
38
|
+
import_resident_id: import_resident_id,
|
39
|
+
resident_type: resident_type,
|
40
|
+
api_name: 'GetPropertyResidents',
|
41
|
+
request_params: EventTracker::ResourceFactory::PmsResident.build_request_params(
|
42
|
+
start_date: request_params[:start_date].nil? ? nil : request_params[:start_date].to_time,
|
43
|
+
end_date: request_params[:end_date].nil? ? nil : request_params[:end_date].to_time,
|
44
|
+
prospect_id: nil,
|
45
|
+
pmc_id: request_params[:pmc_id],
|
46
|
+
remote_id: request_params[:remote_id],
|
47
|
+
traffic_source_id: nil
|
48
|
+
),
|
49
|
+
move_in_date: move_in_date,
|
50
|
+
lease_to: lease_to,
|
51
|
+
lease_from: lease_from,
|
52
|
+
first_name_present: first_name_present,
|
53
|
+
last_name_present: last_name_present,
|
54
|
+
email_present: email_present,
|
55
|
+
phones_count: phones_count,
|
56
|
+
unit_name: unit_name,
|
57
|
+
resident_id: resident_id,
|
58
|
+
move_in_report_type: 'amsi_api',
|
59
|
+
error: error
|
60
|
+
)
|
61
|
+
)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.track_pms_prospect_event(
|
66
|
+
remote_lease_id: nil,
|
67
|
+
request_params:,
|
68
|
+
first_name_present:,
|
69
|
+
last_name_present:,
|
70
|
+
email_present:,
|
71
|
+
phone_present:,
|
72
|
+
contact_date: nil,
|
73
|
+
contact_source: nil,
|
74
|
+
remote_prospect_id: nil,
|
75
|
+
error: nil
|
76
|
+
)
|
77
|
+
EventTracker.track_process_events(name: IMPORT_PMS_PROSPECT_EVENT) do |events|
|
78
|
+
events.add_imported_event(
|
79
|
+
EventTracker::ResourceFactory.build_pms_prospect(
|
80
|
+
billing_import: EventTracker::BillingImportFactory.build_billing_import(
|
81
|
+
property_id: request_params[:property_id],
|
82
|
+
billing_config_id: request_params[:billing_config_id],
|
83
|
+
remote_id: request_params[:remote_id],
|
84
|
+
pms_type: 'amsi',
|
85
|
+
import_id: request_params[:import_id],
|
86
|
+
pmc_id: request_params[:pmc_id],
|
87
|
+
service: 'amsi'
|
88
|
+
),
|
89
|
+
remote_lease_id: remote_lease_id,
|
90
|
+
import_resident_id: request_params[:import_resident_id] || '',
|
91
|
+
resident_type: 'PRIMARY',
|
92
|
+
api_name: 'GetPropertyResidents',
|
93
|
+
request_params: EventTracker::ResourceFactory::PmsProspect.build_request_params(
|
94
|
+
pmc_id: request_params[:pmc_id],
|
95
|
+
remote_id: request_params[:remote_id],
|
96
|
+
prospect_id: request_params[:prospect_id],
|
97
|
+
first_name_present: first_name_present,
|
98
|
+
last_name_present: last_name_present,
|
99
|
+
email_present: email_present,
|
100
|
+
phone_present: phone_present,
|
101
|
+
),
|
102
|
+
contact_date: contact_date,
|
103
|
+
contact_source: contact_source,
|
104
|
+
remote_prospect_id: remote_prospect_id,
|
105
|
+
move_in_report_type: 'amsi_api',
|
106
|
+
error: error
|
107
|
+
)
|
108
|
+
)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'amsi/validator'
|
2
|
+
require 'amsi/utils/snowflake_event_tracker'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Amsi
|
6
|
+
module Validator
|
7
|
+
class Base
|
8
|
+
def send_pms_resident_event(lease:, params:, error_message: nil)
|
9
|
+
remote_lease_id, lease_to, lease_from = [ nil ] * 3
|
10
|
+
if lease
|
11
|
+
unit_name = lease.unit_id
|
12
|
+
move_in_date = lease.move_in_date
|
13
|
+
else
|
14
|
+
unit_name = ''
|
15
|
+
move_in_date = nil
|
16
|
+
end
|
17
|
+
lease.occupants.each_with_index do |occupant, index|
|
18
|
+
Utils::SnowflakeEventTracker.track_pms_resident_event(
|
19
|
+
remote_lease_id: remote_lease_id,
|
20
|
+
import_resident_id: import_resident_id(params),
|
21
|
+
resident_type: index == 0 ? 'PRIMARY': 'ROOMMATE',
|
22
|
+
request_params: pms_resident_request_params(params),
|
23
|
+
move_in_date: move_in_date&.to_time,
|
24
|
+
lease_to: lease_to,
|
25
|
+
lease_from: lease_from,
|
26
|
+
first_name_present: is_present?(occupant.first_name),
|
27
|
+
last_name_present: is_present?(occupant.last_name),
|
28
|
+
email_present: is_present?(occupant.email),
|
29
|
+
phones_count: [occupant.phone_1, occupant.phone_2].compact.count,
|
30
|
+
unit_name: unit_name,
|
31
|
+
resident_id: occupant.resident_id,
|
32
|
+
error: error_message
|
33
|
+
)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def pms_resident_request_params(params)
|
38
|
+
{
|
39
|
+
start_date: params[:start_date]&.to_time,
|
40
|
+
end_date: params[:end_date]&.to_time,
|
41
|
+
prospect_id: nil,
|
42
|
+
pmc_id: params[:pmc_id],
|
43
|
+
remote_id: params[:remote_id],
|
44
|
+
traffic_source_id: nil,
|
45
|
+
import_id: params[:import_id],
|
46
|
+
billing_config_id: params[:billing_config]&.id,
|
47
|
+
property_id: params[:billing_config]&.property_id
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def send_pms_prospect_event(lease:, params:, error_message: nil)
|
52
|
+
lease.occupants.each do |occupant|
|
53
|
+
Utils::SnowflakeEventTracker.track_pms_prospect_event(
|
54
|
+
remote_lease_id: nil,
|
55
|
+
request_params: pms_prospect_request_params(params),
|
56
|
+
first_name_present: is_present?(occupant.first_name),
|
57
|
+
last_name_present: is_present?(occupant.last_name),
|
58
|
+
email_present: is_present?(occupant.email),
|
59
|
+
phone_present: is_present?(occupant.phone_1) || is_present?(occupant.phone_2),
|
60
|
+
contact_date: nil,
|
61
|
+
contact_source: lease.lead_source_code,
|
62
|
+
remote_prospect_id: nil,
|
63
|
+
error: error_message
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def pms_prospect_request_params(params)
|
69
|
+
{
|
70
|
+
billing_config_id: params[:billing_config]&.id,
|
71
|
+
property_id: params[:billing_config]&.property_id,
|
72
|
+
remote_id: params[:remote_id],
|
73
|
+
import_id: params[:import_id],
|
74
|
+
pmc_id: params[:pmc_id],
|
75
|
+
import_resident_id: import_resident_id(params),
|
76
|
+
prospect_id: nil
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def import_resident_id(params)
|
81
|
+
"#{params[:import_id] || ''}-#{SecureRandom.alphanumeric(15)}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def is_present?(value)
|
85
|
+
if value.is_a?(String)
|
86
|
+
!value.strip.empty?
|
87
|
+
elsif value.is_a?(Hash) || value.is_a?(Array)
|
88
|
+
!(value.empty? || value.nil?)
|
89
|
+
else
|
90
|
+
!value.nil?
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'amsi/validator'
|
2
|
+
require_relative 'base'
|
3
|
+
|
4
|
+
module Amsi
|
5
|
+
module Validator
|
6
|
+
class ProspectEventValidator < Base
|
7
|
+
|
8
|
+
def initialize(response = nil)
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate!
|
12
|
+
end
|
13
|
+
|
14
|
+
# Send parased response's individual prospect data to Snowflake via event tracker
|
15
|
+
def send_event(lease, params)
|
16
|
+
send_pms_prospect_event(lease: lease, params: params || {})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'amsi/error/bad_request'
|
2
|
+
|
3
|
+
module Amsi
|
4
|
+
module Validator
|
5
|
+
# Ensure there are no errors in the wrapped contents of the body.
|
6
|
+
#
|
7
|
+
# This validator works on responses that have the following format:
|
8
|
+
#
|
9
|
+
# <?xml version="1.0" encoding="utf-8"?>
|
10
|
+
# <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
11
|
+
# <soap:Body>
|
12
|
+
# <GetPropertyResidentsResponse xmlns="http://tempuri.org/">
|
13
|
+
# <GetPropertyResidentsResult><InternetTrafficResponse><webservice>Leasing</webservice><webmethod>GetPropertyResidents</webmethod><Error><ErrorCode>1</ErrorCode><ErrorDescription>Validation of user credentials failed.</ErrorDescription></Error></InternetTrafficResponse></GetPropertyResidentsResult>
|
14
|
+
# </GetPropertyResidentsResponse>
|
15
|
+
# </soap:Body>
|
16
|
+
# </soap:Envelope>
|
17
|
+
|
18
|
+
class RequestErrors
|
19
|
+
# @param response [Hash<String, Object>] the XML response parsed into a
|
20
|
+
# Hash
|
21
|
+
def initialize(response)
|
22
|
+
@response = response
|
23
|
+
end
|
24
|
+
|
25
|
+
# @raise [Amsi::Error::BadRequest] if the response has an error
|
26
|
+
# node in the contents
|
27
|
+
def validate!
|
28
|
+
return unless error?
|
29
|
+
raise Amsi::Error::BadRequest.new([error])
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
attr_reader :response
|
35
|
+
|
36
|
+
def error_contents
|
37
|
+
body = response['soap:Envelope']['soap:Body']
|
38
|
+
response_key = body.keys.detect { |key| key !~ /^xmlns/ }
|
39
|
+
contents_response = body[response_key]
|
40
|
+
result_key = contents_response.keys.grep(/Result$/).first
|
41
|
+
escaped_result = contents_response[result_key]
|
42
|
+
parsed_result = MultiXml.parse(escaped_result)
|
43
|
+
|
44
|
+
parsed_result['InternetTrafficResponse']
|
45
|
+
end
|
46
|
+
|
47
|
+
def error?
|
48
|
+
error_contents
|
49
|
+
end
|
50
|
+
|
51
|
+
def error
|
52
|
+
error = error_contents['Error']
|
53
|
+
|
54
|
+
Struct.new(:message, :error_code).new(
|
55
|
+
error['ErrorDescription'],
|
56
|
+
error['ErrorCode']
|
57
|
+
)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Amsi
|
2
|
+
module Validator
|
3
|
+
# Ensure there is no 'Fault' node in the response body
|
4
|
+
class RequestFault
|
5
|
+
FaultDetails = Struct.new(:custom_message, :exception_message, :log_id)
|
6
|
+
private_constant :FaultDetails
|
7
|
+
|
8
|
+
# @param response [Hash<String, Object>] the XML response parsed into a
|
9
|
+
# Hash
|
10
|
+
def initialize(response)
|
11
|
+
@response = response
|
12
|
+
end
|
13
|
+
|
14
|
+
# @raise [Amsi::Error::RequestFault] if the response has a fault
|
15
|
+
def validate!
|
16
|
+
return unless error?
|
17
|
+
raise Error::RequestFault.new(error_message, fault_code, details)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :response
|
23
|
+
|
24
|
+
def fault
|
25
|
+
response['soap:Envelope']['soap:Body']['soap:Fault']
|
26
|
+
end
|
27
|
+
|
28
|
+
def details
|
29
|
+
return unless fault['detail']
|
30
|
+
app_fault = fault['detail'].values.first
|
31
|
+
FaultDetails.new(
|
32
|
+
app_fault['CustomMessage'],
|
33
|
+
app_fault['ExceptionMessage'],
|
34
|
+
app_fault['LogId']
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
def error?
|
39
|
+
!fault.nil?
|
40
|
+
end
|
41
|
+
|
42
|
+
def fault_code
|
43
|
+
return unless fault
|
44
|
+
code = fault['faultcode']
|
45
|
+
code = code['__content__'] if code.is_a?(Hash)
|
46
|
+
code.strip
|
47
|
+
end
|
48
|
+
|
49
|
+
def error_message
|
50
|
+
return unless fault
|
51
|
+
fault_string = fault['faultstring']
|
52
|
+
fault_string = fault_string['__content__'] if fault_string.is_a?(Hash)
|
53
|
+
fault_string.strip
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|