markety 1.4.3 → 2.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 +5 -13
- data/.gitignore +3 -0
- data/README.md +66 -15
- data/Rakefile +1 -1
- data/example_xml/get_lead_failure.xml +42 -0
- data/example_xml/get_lead_success.xml +63 -0
- data/example_xml/get_lead_success_two_leads.xml +88 -0
- data/example_xml/list_op_add_to_list_success.xml +42 -0
- data/example_xml/list_op_is_member_of_list_failure.xml +50 -0
- data/example_xml/list_op_is_member_of_list_success.xml +51 -0
- data/example_xml/list_op_remove_from_list_failure.xml +50 -0
- data/example_xml/list_op_remove_from_list_success.xml +42 -0
- data/example_xml/sync_lead_failure.xml +60 -0
- data/example_xml/sync_lead_success.xml +78 -0
- data/lib/markety.rb +26 -1
- data/lib/markety/authentication_header.rb +3 -3
- data/lib/markety/client.rb +35 -141
- data/lib/markety/command.rb +11 -0
- data/lib/markety/command/get_lead.rb +27 -0
- data/lib/markety/command/list_operation.rb +68 -0
- data/lib/markety/command/sync_lead.rb +55 -0
- data/lib/markety/enums.rb +18 -16
- data/lib/markety/lead.rb +69 -0
- data/lib/markety/lead_key.rb +5 -4
- data/lib/markety/response.rb +15 -0
- data/lib/markety/response/generic_response.rb +45 -0
- data/lib/markety/response/get_lead_response.rb +35 -0
- data/lib/markety/response/list_operation_response.rb +30 -0
- data/lib/markety/response/response_factory.rb +27 -0
- data/lib/markety/response/sync_lead_response.rb +32 -0
- data/lib/markety/version.rb +1 -3
- data/spec/markety/lead_key_spec.rb +10 -11
- data/spec/markety/{lead_record_spec.rb → lead_spec.rb} +13 -30
- data/spec/markety/response/get_lead_response_spec.rb +170 -0
- data/spec/markety/response/list_operation_response_spec.rb +76 -0
- data/spec/markety/response/sync_lead_response_spec.rb +107 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/savon_helper.rb +14 -0
- metadata +61 -33
- data/lib/markety/lead_record.rb +0 -73
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'markety/command/get_lead'
|
2
|
+
require 'markety/command/sync_lead'
|
3
|
+
require 'markety/command/list_operation'
|
4
|
+
|
5
|
+
module Markety
|
6
|
+
# All Command submodules are included in the Markety::Client class.
|
7
|
+
# They are implemented in separate modules only for maintainability
|
8
|
+
# (specifically, to keep Markety::Client from becoming huge).
|
9
|
+
module Command
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Markety
|
2
|
+
module Command
|
3
|
+
|
4
|
+
# GetLead commands return Response::GetLeadResponse objects
|
5
|
+
module GetLead
|
6
|
+
|
7
|
+
# IDs are unique per lead, so the response can only contain one lead.
|
8
|
+
def get_lead_by_idnum(idnum)
|
9
|
+
get_lead(LeadKey.new(LeadKeyType::IDNUM, idnum))
|
10
|
+
end
|
11
|
+
|
12
|
+
# Multiple leads can share an email address,
|
13
|
+
# so this may result in more than one lead in the response.
|
14
|
+
def get_leads_by_email(email)
|
15
|
+
get_lead(LeadKey.new(LeadKeyType::EMAIL, email))
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def get_lead(lead_key)
|
22
|
+
send_request(:get_lead, {"leadKey" => lead_key.to_hash})
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Markety
|
2
|
+
module Command
|
3
|
+
# ListOperation commands return Response::ListOperationResponse objects.
|
4
|
+
#
|
5
|
+
# In all these functions:
|
6
|
+
# * An exception will be thrown if you pass in a Lead and it doesn't have an idnum
|
7
|
+
# * Allowed options:
|
8
|
+
# [strict] From {Marketo's docs}[http://developers.marketo.com/documentation/soap/listoperation/]: <em>Strict mode will fail for the entire operation if any subset of the call fails. Non-strict mode will complete everything it can and return errors for anything that failed.</em>
|
9
|
+
#
|
10
|
+
# All ListOp failures look similar, and all successes look similar.
|
11
|
+
module ListOperation
|
12
|
+
|
13
|
+
# note: If you add something that's already in the list, ListOperationResponse::list_operation_success? will be +true+
|
14
|
+
def add_to_list(list_name, lead_or_idnum, options={})
|
15
|
+
list_operation(list_name, "ADDTOLIST", lead_or_idnum, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
# note: If you remove something that's not in the list, ListOperationResponse::list_operation_success? will be +false+
|
19
|
+
def remove_from_list(list_name, lead_or_idnum, options={})
|
20
|
+
list_operation(list_name, "REMOVEFROMLIST", lead_or_idnum, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
# ListOperationResponse::list_operation_success? is the result of this query
|
24
|
+
def is_member_of_list(list_name, lead_or_idnum, options={})
|
25
|
+
list_operation(list_name, "ISMEMBEROFLIST", lead_or_idnum, options)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
ADD_TO = 'ADDTOLIST' # :nodoc:
|
31
|
+
REMOVE_FROM = 'REMOVEFROMLIST' # :nodoc:
|
32
|
+
IS_MEMBER_OF = 'ISMEMBEROFLIST' # :nodoc:
|
33
|
+
private_constant :ADD_TO
|
34
|
+
private_constant :REMOVE_FROM
|
35
|
+
private_constant :IS_MEMBER_OF
|
36
|
+
|
37
|
+
ALLOWED_OPS = [ADD_TO,REMOVE_FROM,IS_MEMBER_OF] # :nodoc:
|
38
|
+
private_constant :ALLOWED_OPS
|
39
|
+
|
40
|
+
def list_operation(list_name, list_operation_type, lead_or_idnum, options)
|
41
|
+
raise "Unknown list operation type" unless ALLOWED_OPS.include?(list_operation_type)
|
42
|
+
idnum = lead_or_idnum
|
43
|
+
if lead_or_idnum.is_a? Markety::Lead
|
44
|
+
raise "Lead has no idnum, which this command needs" unless lead_or_idnum.idnum
|
45
|
+
idnum = lead_or_idnum.idnum
|
46
|
+
end
|
47
|
+
|
48
|
+
strict = options.has_key?(:strict) ? !!options[:strict] : true
|
49
|
+
|
50
|
+
strict = !!options[:strict]
|
51
|
+
send_request(:list_operation, {
|
52
|
+
list_operation: list_operation_type,
|
53
|
+
strict: strict,
|
54
|
+
list_key: {
|
55
|
+
key_type: 'MKTOLISTNAME',
|
56
|
+
key_value: list_name
|
57
|
+
},
|
58
|
+
list_member_list: {
|
59
|
+
lead_key: [{
|
60
|
+
key_type: 'IDNUM',
|
61
|
+
key_value: idnum
|
62
|
+
}]
|
63
|
+
}
|
64
|
+
})
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Markety
|
2
|
+
module Command
|
3
|
+
# SyncLead commands return Response::SyncResponse objects
|
4
|
+
module SyncLead
|
5
|
+
|
6
|
+
# Create a new lead or update an existing lead in Marketo.
|
7
|
+
# * +lead+ - the lead to create or sync
|
8
|
+
# * +sync_method+ - a SyncMethod enum value
|
9
|
+
def sync_lead(lead, sync_method)
|
10
|
+
request_hash = create_sync_lead_request_hash(lead,sync_method)
|
11
|
+
send_request(:sync_lead, request_hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def create_sync_lead_request_hash(lead, sync_method)
|
17
|
+
raise "missing sync method" unless sync_method
|
18
|
+
|
19
|
+
case sync_method
|
20
|
+
when SyncMethod::MARKETO_ID
|
21
|
+
raise "lead has no idnum" unless lead.idnum
|
22
|
+
when SyncMethod::FOREIGN_ID
|
23
|
+
raise "lead has no foreign_sys_person_id" unless lead.foreign_sys_person_id
|
24
|
+
when SyncMethod::EMAIL
|
25
|
+
raise "lead has no email" unless lead.email
|
26
|
+
else
|
27
|
+
raise "unrecognized Markety::SyncMethod '#{sync_method}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
# note from gbirchmeier:
|
31
|
+
# A Marketo support guy told me the fields must come in a very particular order,
|
32
|
+
# thus why this flow is a little janky.
|
33
|
+
# I've since come to doubt this advice (the Marketo support guys do not appear to
|
34
|
+
# actually be very technical), but I'm not going to fix something that's not broke.
|
35
|
+
|
36
|
+
request_hash = {
|
37
|
+
lead_record: { },
|
38
|
+
return_lead: true,
|
39
|
+
}
|
40
|
+
|
41
|
+
# id fields must come first in lead_record
|
42
|
+
request_hash[:lead_record][:id]=lead.idnum if sync_method==SyncMethod::MARKETO_ID
|
43
|
+
use_foreign_id = lead.foreign_sys_person_id && [SyncMethod::MARKETO_ID,SyncMethod::FOREIGN_ID].include?(sync_method)
|
44
|
+
request_hash[:lead_record][:foreignSysPersonId]=lead.foreign_sys_person_id if use_foreign_id
|
45
|
+
request_hash[:lead_record]["Email"]=lead.email if lead.email
|
46
|
+
|
47
|
+
# now lead attributes (which must be ordered name/type/value) (type is optional, but must precede value if present)
|
48
|
+
request_hash[:lead_record][:lead_attribute_list] = { attribute: lead.send(:attributes_soap_array) }
|
49
|
+
|
50
|
+
request_hash
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/markety/enums.rb
CHANGED
@@ -1,21 +1,23 @@
|
|
1
1
|
module Markety
|
2
|
-
# Types of
|
3
|
-
|
4
|
-
ADD_TO = 'ADDTOLIST'
|
5
|
-
REMOVE_FROM = 'REMOVEFROMLIST'
|
6
|
-
IS_MEMBER_OF = 'ISMEMBEROFLIST'
|
7
|
-
end
|
8
|
-
|
9
|
-
# Types of keys that can be used to look up a lead
|
2
|
+
# Types of keys that can be used to look up a lead.
|
3
|
+
# (Other key types exist, but Markety only supports these at this time.)
|
10
4
|
module LeadKeyType
|
11
5
|
IDNUM = "IDNUM"
|
12
|
-
COOKIE = "COOKIE"
|
13
6
|
EMAIL = "EMAIL"
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
7
|
+
|
8
|
+
# COOKIE = "COOKIE"
|
9
|
+
# LEADOWNEREMAIL = "LEADOWNEREMAIL"
|
10
|
+
# SFDCACCOUNTID = "SFDCACCOUNTID"
|
11
|
+
# SFDCCONTACTID = "SFDCCONTACTID"
|
12
|
+
# SFDCLEADID = "SFDCLEADID"
|
13
|
+
# SFDCLEADOWNERID = "SFDCLEADOWNERID"
|
14
|
+
# SFDCOPPTYID = "SFDCOPPTYID"
|
15
|
+
end
|
16
|
+
|
17
|
+
# a parameter type to Markety::Command::SyncLead
|
18
|
+
module SyncMethod
|
19
|
+
MARKETO_ID = "MARKETO_ID"
|
20
|
+
FOREIGN_ID = "FOREIGN_ID"
|
21
|
+
EMAIL = "EMAIL"
|
20
22
|
end
|
21
|
-
end
|
23
|
+
end
|
data/lib/markety/lead.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Markety
|
2
|
+
# Represents a record of the data known about a lead within Marketo
|
3
|
+
class Lead
|
4
|
+
attr_reader :types, :idnum, :attributes
|
5
|
+
attr_accessor :foreign_sys_person_id, :email
|
6
|
+
|
7
|
+
def initialize(email:nil, idnum:nil, foreign_sys_person_id:nil)
|
8
|
+
@idnum = idnum
|
9
|
+
@foreign_sys_person_id = foreign_sys_person_id
|
10
|
+
@email = email
|
11
|
+
@attributes = {}
|
12
|
+
@types = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
@attributes==other.send(:attributes) &&
|
17
|
+
@idnum==other.idnum &&
|
18
|
+
@email==other.email &&
|
19
|
+
@foreign_sys_person_id==other.foreign_sys_person_id
|
20
|
+
end
|
21
|
+
|
22
|
+
# hydrates an instance from a savon hash returned from the marketo API
|
23
|
+
def self.from_hash(savon_hash)
|
24
|
+
lead = Lead.new(email: savon_hash[:email], idnum:savon_hash[:id].to_i)
|
25
|
+
|
26
|
+
unless savon_hash[:lead_attribute_list].nil?
|
27
|
+
if savon_hash[:lead_attribute_list][:attribute].kind_of? Hash
|
28
|
+
attributes = [savon_hash[:lead_attribute_list][:attribute]]
|
29
|
+
else
|
30
|
+
attributes = savon_hash[:lead_attribute_list][:attribute]
|
31
|
+
end
|
32
|
+
|
33
|
+
attributes.each do |attribute|
|
34
|
+
lead.set_attribute(attribute[:attr_name], attribute[:attr_value], attribute[:attr_type])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
lead
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# update the value of the named attribute
|
43
|
+
def set_attribute(name, value, type = "string")
|
44
|
+
@attributes[name] = value
|
45
|
+
@types[name] = type
|
46
|
+
end
|
47
|
+
|
48
|
+
# get the value for the named attribute
|
49
|
+
def get_attribute(name)
|
50
|
+
@attributes[name]
|
51
|
+
end
|
52
|
+
|
53
|
+
# get the type of the named attribute
|
54
|
+
def get_attribute_type(name)
|
55
|
+
@types[name]
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
private
|
60
|
+
def attributes_soap_array()
|
61
|
+
arr = []
|
62
|
+
@attributes.each_pair do |name,value|
|
63
|
+
arr << {attr_name: name, attr_type: self.get_attribute_type(name), attr_value: value }
|
64
|
+
end
|
65
|
+
arr
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/markety/lead_key.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Markety
|
2
2
|
# Encapsulates a key used to look up or describe a specific marketo lead.
|
3
|
+
# Markety users should not use this class directly.
|
3
4
|
class LeadKey
|
4
|
-
# - *key_type* the type of key to use see LeadKeyType
|
5
|
-
# - *key_value*
|
5
|
+
# - *key_type* - value of LeadKeyType enum; the type of key to use see LeadKeyType
|
6
|
+
# - *key_value* - a string value for the given type
|
6
7
|
def initialize(key_type, key_value)
|
7
8
|
@key_type = key_type
|
8
9
|
@key_value = key_value
|
9
10
|
end
|
10
11
|
|
11
|
-
# get the key type
|
12
|
+
# get the key type (a LeadKeyType enum value)
|
12
13
|
def key_type
|
13
14
|
@key_type
|
14
15
|
end
|
@@ -26,4 +27,4 @@ module Markety
|
|
26
27
|
}
|
27
28
|
end
|
28
29
|
end
|
29
|
-
end
|
30
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'markety/response/response_factory'
|
2
|
+
|
3
|
+
require 'markety/response/generic_response'
|
4
|
+
require 'markety/response/get_lead_response'
|
5
|
+
require 'markety/response/sync_lead_response'
|
6
|
+
require 'markety/response/list_operation_response'
|
7
|
+
|
8
|
+
|
9
|
+
module Markety
|
10
|
+
# Each Command returns a corresponding Response.
|
11
|
+
# All Response classes are derived from GenericResponse,
|
12
|
+
# which contains some common accessor methods.
|
13
|
+
module Response
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Markety
|
2
|
+
module Response
|
3
|
+
|
4
|
+
# Parent class for all response types.
|
5
|
+
#
|
6
|
+
# SOAP requests sent by Markety result in either
|
7
|
+
# a <tt>Savon::Response</tt> or a <tt>Savon::SOAPFault</tt>.
|
8
|
+
# This class hides those boring details from you,
|
9
|
+
# unless you want to use its methods to see them.
|
10
|
+
class GenericResponse
|
11
|
+
#if the response is a <tt>Savon::SOAPFault</tt>, this is its error message
|
12
|
+
attr_reader :error_message
|
13
|
+
|
14
|
+
# * +cmd_type+ - a symbol
|
15
|
+
# * +response+ - a <tt>Savon::Response</tt> or a <tt>Savon::SOAPFault</tt>
|
16
|
+
def initialize(cmd_type,response)
|
17
|
+
@response = response
|
18
|
+
@success = response.is_a? Savon::Response
|
19
|
+
@error_message = @success ? nil : response.to_s
|
20
|
+
end
|
21
|
+
|
22
|
+
# True if Marketo's response indicates that the SOAP request
|
23
|
+
# was successful.
|
24
|
+
#
|
25
|
+
# *Note:* This is not the same as the a result from
|
26
|
+
# a Marketo command itself! To see the command's result,
|
27
|
+
# consult the command-specific Response class.
|
28
|
+
def success?
|
29
|
+
@success
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the xml from the underlying <tt>Savon::Response</tt> or
|
33
|
+
# <tt>Savon::SOAPFault</tt>
|
34
|
+
def to_xml
|
35
|
+
@success ? @response.to_xml : @response.http.raw_body
|
36
|
+
end
|
37
|
+
|
38
|
+
# The underlying <tt>Savon::Response</tt> or
|
39
|
+
# <tt>Savon::SOAPFault</tt>'s content as a hash
|
40
|
+
def to_hash
|
41
|
+
@response.to_hash
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'markety/lead'
|
2
|
+
require 'markety/response/generic_response'
|
3
|
+
|
4
|
+
module Markety
|
5
|
+
module Response
|
6
|
+
# Response class for Command::GetLead calls.
|
7
|
+
class GetLeadResponse < GenericResponse
|
8
|
+
# Array of leads returned by the GetLead command
|
9
|
+
attr_reader :leads
|
10
|
+
|
11
|
+
def initialize(response)
|
12
|
+
super(:get_lead_response,response)
|
13
|
+
h = self.to_hash
|
14
|
+
@leads = []
|
15
|
+
|
16
|
+
if self.success?
|
17
|
+
count = h[:success_get_lead][:result][:count].to_i
|
18
|
+
lead_hashes = h[:success_get_lead][:result][:lead_record_list][:lead_record]
|
19
|
+
lead_hashes = [lead_hashes] if count==1
|
20
|
+
lead_hashes.each {|leadhash| @leads << ::Markety::Lead.from_hash(leadhash) }
|
21
|
+
else
|
22
|
+
# overwrite super's crap error message with useful one
|
23
|
+
@error_message = h[:fault][:detail][:service_exception][:message]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Convenience shortcut to get first element of #leads (or nil if none).
|
28
|
+
# Appropriate for responses to Command::GetLead#get_lead_by_idnum, which cannot
|
29
|
+
# result in more than one lead.
|
30
|
+
def lead
|
31
|
+
@leads.first
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'markety/response/generic_response'
|
2
|
+
|
3
|
+
module Markety
|
4
|
+
module Response
|
5
|
+
# Response class for Command::ListOperation calls
|
6
|
+
class ListOperationResponse < GenericResponse
|
7
|
+
|
8
|
+
def initialize(response)
|
9
|
+
super(:list_operation_response,response)
|
10
|
+
@list_operation_success = false
|
11
|
+
|
12
|
+
if self.success?
|
13
|
+
h = self.to_hash
|
14
|
+
@list_operation_success = h[:success_list_operation][:result][:success]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Whether the operation was successful.
|
19
|
+
#
|
20
|
+
# *Note:* this is not the same as parent's success? method.
|
21
|
+
# For list operations, success? almost always true
|
22
|
+
# (because Marketo accepted the request and gave you a response).
|
23
|
+
def list_operation_success?
|
24
|
+
@list_operation_success
|
25
|
+
end
|
26
|
+
alias list_op_success? list_operation_success?
|
27
|
+
alias lop_success? list_operation_success?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'markety/response/generic_response'
|
2
|
+
require 'markety/response/get_lead_response'
|
3
|
+
require 'markety/response/sync_lead_response'
|
4
|
+
require 'markety/response/list_operation_response'
|
5
|
+
|
6
|
+
module Markety
|
7
|
+
module Response
|
8
|
+
# Factory that creates the appropriate Response object depending on the command type
|
9
|
+
class ResponseFactory
|
10
|
+
|
11
|
+
# Create the appropriate Response object depending on the command type
|
12
|
+
def self.create_response(cmd_type,savon_response)
|
13
|
+
case cmd_type
|
14
|
+
when :get_lead
|
15
|
+
GetLeadResponse.new(savon_response)
|
16
|
+
when :sync_lead
|
17
|
+
SyncLeadResponse.new(savon_response)
|
18
|
+
when :list_operation
|
19
|
+
ListOperationResponse.new(savon_response)
|
20
|
+
else
|
21
|
+
GenericResponse.new(cmd_type,savon_response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|