njones-marketo 1.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ module Rapleaf
2
+ module Marketo
3
+ # This class exists only to encapsulate the authentication header part of a soap request to marketo
4
+ # Marketo requires a somewhat complex calculation of an encrypted signature and so it seemed sensible to pull this code out here
5
+ class AuthenticationHeader
6
+ DIGEST = OpenSSL::Digest::Digest.new('sha1')
7
+
8
+ def initialize(access_key, secret_key, time = DateTime.now)
9
+ @access_key = access_key
10
+ @secret_key = secret_key
11
+ @time = time
12
+ end
13
+
14
+ public
15
+ # time should be a DateTime instance
16
+ def set_time(time)
17
+ @time = time
18
+ end
19
+
20
+ def get_mktows_user_id
21
+ @access_key
22
+ end
23
+
24
+ def get_request_signature
25
+ calculate_signature
26
+ end
27
+
28
+ def get_request_timestamp
29
+ @time.to_s
30
+ end
31
+
32
+ def to_hash
33
+ {
34
+ "mktowsUserId" => get_mktows_user_id,
35
+ "requestSignature" => get_request_signature,
36
+ "requestTimestamp" => get_request_timestamp
37
+ }
38
+ end
39
+
40
+ private
41
+ def calculate_signature
42
+ request_timestamp = get_request_timestamp
43
+ string_to_encrypt = request_timestamp + @access_key
44
+ OpenSSL::HMAC.hexdigest(DIGEST, @secret_key, string_to_encrypt)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,202 @@
1
+ require File.expand_path('authentication_header', File.dirname(__FILE__))
2
+
3
+ module Rapleaf
4
+ module Marketo
5
+
6
+ def self.new_client(access_key, secret_key, api_version='1.7', api_subdomain='na-c')
7
+
8
+ api_version = api_version.sub(".", "_")
9
+ client = Savon::Client.new do
10
+ wsdl.endpoint = "https://#{api_subdomain}.marketo.com/soap/mktows/#{api_version}"
11
+ wsdl.document = "http://app.marketo.com/soap/mktows/#{api_version}?WSDL"
12
+ http.read_timeout = 90
13
+ http.open_timeout = 90
14
+ http.headers = {"Connection" => "Keep-Alive"}
15
+ end
16
+
17
+ Client.new(client, Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key))
18
+ end
19
+
20
+ # = The client for talking to marketo
21
+ # based on the SOAP wsdl file: <i>http://app.marketo.com/soap/mktows/1_4?WSDL</i>
22
+ #
23
+ # Usage:
24
+ #
25
+ # client = Rapleaf::Marketo.new_client(<access_key>, <secret_key>)
26
+ #
27
+ # == get_lead_by_email:
28
+ #
29
+ # lead_record = client.get_lead_by_email('sombody@examnple.com')
30
+ #
31
+ # puts lead_record.idnum
32
+ #
33
+ # puts lead_record.get_attribute('FirstName')
34
+ #
35
+ # puts lead_record.get_attribute('LastName')
36
+ #
37
+ # == sync_lead: (update)
38
+ #
39
+ # lead_record = client.sync_lead('example@rapleaf.com', 'Joe', 'Smith', 'Company 1', '415 911')
40
+ #
41
+ # == sync_lead_record: (update with custom fields)
42
+ #
43
+ # lead_record = Rapleaf::Marketo::LeadRecord.new('harry@rapleaf.com')
44
+ #
45
+ # lead_record.set_attribute('FirstName', 'harry')
46
+ #
47
+ # lead_record.set_attribute('LastName', 'smith')
48
+ #
49
+ # lead_record.set_attribute('Email', 'harry@somesite.com')
50
+ #
51
+ # lead_record.set_attribute('Company', 'Rapleaf')
52
+ #
53
+ # lead_record.set_attribute('MobilePhone', '123 456')
54
+ #
55
+ # response = client.sync_lead_record(lead_record)
56
+ class Client
57
+ # This constructor is used internally, create your client with *Rapleaf::Marketo.new_client(<access_key>, <secret_key>)*
58
+ def initialize(savon_client, authentication_header)
59
+ @client = savon_client
60
+ @header = authentication_header
61
+ end
62
+
63
+ public
64
+
65
+ def get_lead_by_idnum(idnum)
66
+ get_lead(LeadKey.new(LeadKeyType::IDNUM, idnum))
67
+ end
68
+
69
+
70
+ def get_lead_by_email(email)
71
+ get_lead(LeadKey.new(LeadKeyType::EMAIL, email))
72
+ end
73
+
74
+ def set_logger(logger)
75
+ @logger = logger
76
+ end
77
+
78
+ # create (if new) or update (if existing) a lead
79
+ #
80
+ # * email - email address of lead
81
+ # * first - first name of lead
82
+ # * last - surname/last name of lead
83
+ # * company - company the lead is associated with
84
+ # * mobile - mobile/cell phone number
85
+ #
86
+ # returns the LeadRecord instance on success otherwise nil
87
+ def sync_lead(email, first, last, company, mobile)
88
+ lead_record = LeadRecord.new(email)
89
+ lead_record.set_attribute('FirstName', first)
90
+ lead_record.set_attribute('LastName', last)
91
+ lead_record.set_attribute('Email', email)
92
+ lead_record.set_attribute('Company', company)
93
+ lead_record.set_attribute('MobilePhone', mobile)
94
+ sync_lead_record(lead_record)
95
+ end
96
+
97
+ def sync_lead_record(lead_record)
98
+ begin
99
+ attributes = []
100
+ lead_record.each_attribute_pair do |name, value|
101
+ attributes << {:attr_name => name, :attr_type => 'string', :attr_value => value}
102
+ end
103
+
104
+ response = send_request("ns1:paramsSyncLead", {
105
+ :return_lead => true,
106
+ :lead_record =>
107
+ {:email => lead_record.email,
108
+ :lead_attribute_list => {
109
+ :attribute => attributes}}})
110
+ return LeadRecord.from_hash(response[:success_sync_lead][:result][:lead_record])
111
+ rescue Exception => e
112
+ @logger.log(e) if @logger
113
+ return nil
114
+ end
115
+ end
116
+
117
+ def sync_multi_lead_records(lead_records)
118
+ send_records = []
119
+ begin
120
+ lead_records.each do |lead_record|
121
+ attributes = []
122
+ lead_record.each_attribute_pair do |name, value|
123
+ attributes << {:attr_name => name, :attr_type => 'string', :attr_value => value}
124
+ end
125
+
126
+ send_records << {:lead_record =>
127
+ { "Email" => lead_record.email,
128
+ :lead_attribute_list =>
129
+ { :attribute => attributes }
130
+ }
131
+ }
132
+ end
133
+
134
+ response = send_request("ns1:paramsSyncMultipleLeads", {
135
+ :lead_record_list => send_records }) # an array of lead records
136
+
137
+ return response
138
+ rescue Exception => e
139
+ @logger.log(e) if @logger
140
+ return @client.http
141
+ #return nil
142
+ end
143
+ end
144
+
145
+ def add_to_list(list_key, email)
146
+ list_operation(list_key, ListOperationType::ADD_TO, email)
147
+ end
148
+
149
+ def remove_from_list(list_key, email)
150
+ list_operation(list_key, ListOperationType::REMOVE_FROM, email)
151
+ end
152
+
153
+ def is_member_of_list?(list_key, email)
154
+ list_operation(list_key, ListOperationType::IS_MEMBER_OF, email)
155
+ end
156
+
157
+ private
158
+ def list_operation(list_key, list_operation_type, email)
159
+ begin
160
+ response = send_request("ns1:paramsListOperation", {
161
+ :list_operation => list_operation_type,
162
+ :list_key => list_key,
163
+ :strict => 'false',
164
+ :list_member_list => {
165
+ :lead_key => [
166
+ {:key_type => 'EMAIL', :key_value => email}
167
+ ]
168
+ }
169
+ })
170
+ return response
171
+ rescue Exception => e
172
+ @logger.log(e) if @logger
173
+ return nil
174
+ end
175
+ end
176
+
177
+ def get_lead(lead_key)
178
+ begin
179
+ response = send_request("ns1:paramsGetLead", {:lead_key => lead_key.to_hash})
180
+ return LeadRecord.from_hash(response[:success_get_lead][:result][:lead_record_list][:lead_record])
181
+ rescue Exception => e
182
+ @logger.log(e) if @logger
183
+ return nil
184
+ end
185
+ end
186
+
187
+ def send_request(namespace, body)
188
+ @header.set_time(DateTime.now)
189
+ response = request(namespace, body, @header.to_hash)
190
+ response.to_hash
191
+ end
192
+
193
+ def request(namespace, body, header)
194
+ @client.request namespace do |soap|
195
+ soap.namespaces["xmlns:ns1"] = "http://www.marketo.com/mktows/"
196
+ soap.body = body
197
+ soap.header["ns1:AuthenticationHeader"] = header
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,23 @@
1
+ module Rapleaf
2
+ module Marketo
3
+ # Types of operations you can do on a marketo list
4
+ module ListOperationType
5
+ ADD_TO = 'ADDTOLIST'
6
+ REMOVE_FROM = 'REMOVEFROMLIST'
7
+ IS_MEMBER_OF = 'ISMEMBEROFLIST'
8
+ end
9
+
10
+ # Types of keys that can be used to look up a lead
11
+ module LeadKeyType
12
+ IDNUM = "IDNUM"
13
+ COOKIE = "COOKIE"
14
+ EMAIL = "EMAIL"
15
+ LEADOWNEREMAIL = "LEADOWNEREMAIL"
16
+ SFDCACCOUNTID = "SFDCACCOUNTID"
17
+ SFDCCONTACTID = "SFDCCONTACTID"
18
+ SFDCLEADID = "SFDCLEADID"
19
+ SFDCLEADOWNERID = "SFDCLEADOWNERID"
20
+ SFDCOPPTYID = "SFDCOPPTYID"
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ module Rapleaf
2
+ module Marketo
3
+ # Encapsulates a key used to look up or describe a specific marketo lead.
4
+ class LeadKey
5
+ # - *key_type* the type of key to use see LeadKeyType
6
+ # - *key_value* normally a string value for the given type
7
+ def initialize(key_type, key_value)
8
+ @key_type = key_type
9
+ @key_value = key_value
10
+ end
11
+
12
+ # get the key type
13
+ def key_type
14
+ @key_type
15
+ end
16
+
17
+ # get the key value
18
+ def key_value
19
+ @key_value
20
+ end
21
+
22
+ # create a hash from this instance, for sending this object to marketo using savon
23
+ def to_hash
24
+ {
25
+ :key_type => @key_type,
26
+ :key_value => @key_value
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ module Rapleaf
2
+ module Marketo
3
+ # Represents a record of the data known about a lead within marketo
4
+ class LeadRecord
5
+ def initialize(email, idnum = nil)
6
+ @email = email
7
+ @idnum = idnum
8
+ @attributes = {}
9
+ set_attribute('Email', @email)
10
+ end
11
+
12
+ # hydrates an instance from a savon hash returned form the marketo API
13
+ def self.from_hash(savon_hash)
14
+ lead_record = LeadRecord.new(savon_hash[:email], savon_hash[:id].to_i)
15
+ savon_hash[:lead_attribute_list][:attribute].each do |attribute|
16
+ lead_record.set_attribute(attribute[:attr_name], attribute[:attr_value])
17
+ end
18
+ lead_record
19
+ end
20
+
21
+ # get the record idnum
22
+ def idnum
23
+ @idnum
24
+ end
25
+
26
+ # get the record email
27
+ def email
28
+ @email
29
+ end
30
+
31
+ def attributes
32
+ @attributes
33
+ end
34
+
35
+ # update the value of the named attribute
36
+ def set_attribute(name, value)
37
+ @attributes[name] = value
38
+ end
39
+
40
+ # get the value for the named attribute
41
+ def get_attribute(name)
42
+ @attributes[name]
43
+ end
44
+
45
+ # will yield pairs of |attribute_name, attribute_value|
46
+ def each_attribute_pair(&block)
47
+ @attributes.each_pair do |name, value|
48
+ block.call(name, value)
49
+ end
50
+ end
51
+
52
+ def ==(other)
53
+ @attributes == other.attributes &&
54
+ @idnum == other.idnum &&
55
+ @email == other.email
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'savon'
3
+
4
+ Savon.configure do |config|
5
+ config.log = false # disable logging
6
+ end
7
+
8
+ require File.expand_path('marketo/client', File.dirname(__FILE__))
9
+ require File.expand_path('marketo/authentication_header', File.dirname(__FILE__))
10
+ require File.expand_path('marketo/enums', File.dirname(__FILE__))
11
+ require File.expand_path('marketo/lead_key', File.dirname(__FILE__))
12
+ require File.expand_path('marketo/lead_record', File.dirname(__FILE__))
13
+
14
+
15
+
16
+
@@ -0,0 +1,66 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ module Rapleaf
4
+ module Marketo
5
+ ACCESS_KEY = 'ACCESS_KEY'
6
+ SECRET_KEY = 'SECRET_KEY'
7
+
8
+ describe AuthenticationHeader do
9
+ it "should set mktowsUserId to access key" do
10
+ header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY)
11
+ header.get_mktows_user_id.should == ACCESS_KEY
12
+ end
13
+
14
+ it "should set requestSignature" do
15
+ header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY)
16
+ header.get_request_signature.should_not be_nil
17
+ header.get_request_signature.should_not == ''
18
+ end
19
+
20
+ it "should set requestTimestamp in correct format" do
21
+ header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY)
22
+ time = DateTime.new(1998, 1, 17, 20, 15, 1)
23
+ header.set_time(time)
24
+ header.get_request_timestamp().should == '1998-01-17T20:15:01+00:00'
25
+ end
26
+
27
+ it "should calculate encrypted signature" do
28
+ # I got this example of the marketo API docs
29
+
30
+ access_key = 'bigcorp1_461839624B16E06BA2D663'
31
+ secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66'
32
+
33
+ header = Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key)
34
+ header.set_time(DateTime.new(2010, 4, 9, 14, 4, 55, -7/24.0))
35
+
36
+ header.get_request_timestamp.should == '2010-04-09T14:04:54-07:00'
37
+ header.get_request_signature.should == 'ffbff4d4bef354807481e66dc7540f7890523a87'
38
+ end
39
+
40
+ it "should cope if no date is given" do
41
+ header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY)
42
+ expected = DateTime.now
43
+ actual = DateTime.parse(header.get_request_timestamp)
44
+
45
+ expected.year.should == actual.year
46
+ expected.hour.should == actual.hour
47
+ end
48
+
49
+ it "should to_hash correctly" do
50
+ # I got this example from the marketo API docs
51
+
52
+ access_key = 'bigcorp1_461839624B16E06BA2D663'
53
+ secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66'
54
+
55
+ header = Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key)
56
+ header.set_time(DateTime.new(2010, 4, 9, 14, 4, 55, -7/24.0))
57
+
58
+ header.to_hash.should == {
59
+ 'mktowsUserId' => header.get_mktows_user_id,
60
+ 'requestSignature' => header.get_request_signature,
61
+ 'requestTimestamp' => header.get_request_timestamp,
62
+ }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,372 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ module Rapleaf
4
+ module Marketo
5
+
6
+ describe Client do
7
+ EMAIL = "some@email.com"
8
+ IDNUM = 29
9
+ FIRST = 'Joe'
10
+ LAST = 'Smith'
11
+ COMPANY = 'Rapleaf'
12
+ MOBILE = '415 123 456'
13
+ API_KEY = 'API123KEY'
14
+
15
+ context 'Exception handling' do
16
+ it "should return nil if any exception is raised on get_lead request" do
17
+ savon_client = mock('savon_client').as_null_object
18
+ authentication_header = mock('authentication_header').as_null_object
19
+ client = Rapleaf::Marketo::Client.new(savon_client, authentication_header)
20
+ savon_client.should_receive(:request).and_raise Exception
21
+ client.get_lead_by_email(EMAIL).should be_nil
22
+ end
23
+
24
+ it "should return nil if any exception is raised on sync_lead request" do
25
+ savon_client = mock('savon_client').as_null_object
26
+ authentication_header = mock('authentication_header').as_null_object
27
+ client = Rapleaf::Marketo::Client.new(savon_client, authentication_header)
28
+ savon_client.should_receive(:request).and_raise Exception
29
+ client.sync_lead(EMAIL, FIRST, LAST, COMPANY, MOBILE).should be_nil
30
+ end
31
+ end
32
+
33
+ context 'Client interaction' do
34
+ it "should have the correct body format on get_lead_by_idnum" do
35
+ savon_client = mock('savon_client')
36
+ authentication_header = mock('authentication_header')
37
+ client = Rapleaf::Marketo::Client.new(savon_client, authentication_header)
38
+ response_hash = {
39
+ :success_get_lead => {
40
+ :result => {
41
+ :count => 1,
42
+ :lead_record_list => {
43
+ :lead_record => {
44
+ :email => EMAIL,
45
+ :lead_attribute_list => {
46
+ :attribute => [
47
+ {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'},
48
+ {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'},
49
+ {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'},
50
+ {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'}
51
+ ]
52
+ },
53
+ :foreign_sys_type => nil,
54
+ :foreign_sys_person_id => nil,
55
+ :id => IDNUM.to_s
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
61
+ expect_request(savon_client,
62
+ authentication_header,
63
+ equals_matcher(:lead_key => {
64
+ :key_value => IDNUM,
65
+ :key_type => LeadKeyType::IDNUM
66
+ }),
67
+ 'ns1:paramsGetLead',
68
+ response_hash)
69
+ expected_lead_record = LeadRecord.new(EMAIL, IDNUM)
70
+ expected_lead_record.set_attribute('name1', 'val1')
71
+ expected_lead_record.set_attribute('name2', 'val2')
72
+ expected_lead_record.set_attribute('name3', 'val3')
73
+ expected_lead_record.set_attribute('name4', 'val4')
74
+ client.get_lead_by_idnum(IDNUM).should == expected_lead_record
75
+ end
76
+
77
+ it "should have the correct body format on get_lead_by_email" do
78
+ savon_client = mock('savon_client')
79
+ authentication_header = mock('authentication_header')
80
+ client = Rapleaf::Marketo::Client.new(savon_client, authentication_header)
81
+ response_hash = {
82
+ :success_get_lead => {
83
+ :result => {
84
+ :count => 1,
85
+ :lead_record_list => {
86
+ :lead_record => {
87
+ :email => EMAIL,
88
+ :lead_attribute_list => {
89
+ :attribute => [
90
+ {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'},
91
+ {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'},
92
+ {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'},
93
+ {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'}
94
+ ]
95
+ },
96
+ :foreign_sys_type => nil,
97
+ :foreign_sys_person_id => nil,
98
+ :id => IDNUM.to_s
99
+ }
100
+ }
101
+ }
102
+ }
103
+ }
104
+ expect_request(savon_client,
105
+ authentication_header,
106
+ equals_matcher({:lead_key => {
107
+ :key_value => EMAIL,
108
+ :key_type => LeadKeyType::EMAIL}}),
109
+ 'ns1:paramsGetLead',
110
+ response_hash)
111
+ expected_lead_record = LeadRecord.new(EMAIL, IDNUM)
112
+ expected_lead_record.set_attribute('name1', 'val1')
113
+ expected_lead_record.set_attribute('name2', 'val2')
114
+ expected_lead_record.set_attribute('name3', 'val3')
115
+ expected_lead_record.set_attribute('name4', 'val4')
116
+ client.get_lead_by_email(EMAIL).should == expected_lead_record
117
+ end
118
+
119
+ it "should have the correct body format on sync_lead_record" do
120
+ savon_client = mock('savon_client')
121
+ authentication_header = mock('authentication_header')
122
+ client = Rapleaf::Marketo::Client.new(savon_client, authentication_header)
123
+ response_hash = {
124
+ :success_sync_lead => {
125
+ :result => {
126
+ :lead_id => IDNUM,
127
+ :sync_status => {
128
+ :error => nil,
129
+ :status => 'UPDATED',
130
+ :lead_id => IDNUM
131
+ },
132
+ :lead_record => {
133
+ :email => EMAIL,
134
+ :lead_attribute_list => {
135
+ :attribute => [
136
+ {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'},
137
+ {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'},
138
+ {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'},
139
+ {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'}
140
+ ]
141
+ },
142
+ :foreign_sys_type => nil,
143
+ :foreign_sys_person_id => nil,
144
+ :id => IDNUM.to_s
145
+ }
146
+ }
147
+ }
148
+ }
149
+ expect_request(savon_client,
150
+ authentication_header,
151
+ equals_matcher({
152
+ :return_lead => true,
153
+ :lead_record => {
154
+ :email => EMAIL,
155
+ :lead_attribute_list =>
156
+ {
157
+ :attribute => [
158
+ {:attr_value => "val1",
159
+ :attr_name => "name1",
160
+ :attr_type => "string"},
161
+ {:attr_value => "val2",
162
+ :attr_name => "name2",
163
+ :attr_type => "string"},
164
+ {:attr_value => EMAIL,
165
+ :attr_name => "Email",
166
+ :attr_type => "string"}
167
+ ]}}}),
168
+ 'ns1:paramsSyncLead',
169
+ response_hash)
170
+ lead_record = LeadRecord.new(EMAIL)
171
+ lead_record.set_attribute('name1', 'val1')
172
+ lead_record.set_attribute('name2', 'val2')
173
+
174
+ expected_lead_record = LeadRecord.new(EMAIL, IDNUM)
175
+ expected_lead_record.set_attribute('Email', EMAIL)
176
+ expected_lead_record.set_attribute('name1', 'val1')
177
+ expected_lead_record.set_attribute('name2', 'val2')
178
+ expected_lead_record.set_attribute('name3', 'val3')
179
+ expected_lead_record.set_attribute('name4', 'val4')
180
+ client.sync_lead_record(lead_record).should == expected_lead_record
181
+ end
182
+
183
+ it "should have the correct body format on sync_lead" do
184
+ savon_client = mock('savon_client')
185
+ authentication_header = mock('authentication_header')
186
+ client = Rapleaf::Marketo::Client.new(savon_client, authentication_header)
187
+ response_hash = {
188
+ :success_sync_lead => {
189
+ :result => {
190
+ :lead_id => IDNUM,
191
+ :sync_status => {
192
+ :error => nil,
193
+ :status => 'UPDATED',
194
+ :lead_id => IDNUM
195
+ },
196
+ :lead_record => {
197
+ :email => EMAIL,
198
+ :lead_attribute_list => {
199
+ :attribute => [
200
+ {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'},
201
+ {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'},
202
+ {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'},
203
+ {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'}
204
+ ]
205
+ },
206
+ :foreign_sys_type => nil,
207
+ :foreign_sys_person_id => nil,
208
+ :id => IDNUM.to_s
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ expect_request(savon_client,
215
+ authentication_header,
216
+ Proc.new { |actual|
217
+ actual_attribute_list = actual[:lead_record][:lead_attribute_list][:attribute]
218
+ actual[:lead_record][:lead_attribute_list][:attribute] = nil
219
+ expected = {
220
+ :return_lead => true,
221
+ :lead_record => {
222
+ :email => "some@email.com",
223
+ :lead_attribute_list =>
224
+ {
225
+ :attribute => nil}}
226
+ }
227
+ actual.should == expected
228
+ actual_attribute_list.should =~ [
229
+ {:attr_value => FIRST,
230
+ :attr_name => "FirstName",
231
+ :attr_type => "string"},
232
+ {:attr_value => LAST,
233
+ :attr_name => "LastName",
234
+ :attr_type => "string"},
235
+ {:attr_value => EMAIL,
236
+ :attr_name =>"Email",
237
+ :attr_type => "string"},
238
+ {:attr_value => COMPANY,
239
+ :attr_name => "Company",
240
+ :attr_type => "string"},
241
+ {:attr_value => MOBILE,
242
+ :attr_name => "MobilePhone",
243
+ :attr_type => "string"}
244
+ ]
245
+ },
246
+ 'ns1:paramsSyncLead',
247
+ response_hash)
248
+ expected_lead_record = LeadRecord.new(EMAIL, IDNUM)
249
+ expected_lead_record.set_attribute('name1', 'val1')
250
+ expected_lead_record.set_attribute('name2', 'val2')
251
+ expected_lead_record.set_attribute('name3', 'val3')
252
+ expected_lead_record.set_attribute('name4', 'val4')
253
+ client.sync_lead(EMAIL, FIRST, LAST, COMPANY, MOBILE).should == expected_lead_record
254
+ end
255
+
256
+ context "list operations" do
257
+ LIST_KEY = 'awesome leads list'
258
+
259
+ before(:each) do
260
+ @savon_client = mock('savon_client')
261
+ @authentication_header = mock('authentication_header')
262
+ @client = Rapleaf::Marketo::Client.new(@savon_client, @authentication_header)
263
+ end
264
+
265
+ it "should have the correct body format on add_to_list" do
266
+ response_hash = {} # TODO
267
+ expect_request(@savon_client,
268
+ @authentication_header,
269
+ equals_matcher({
270
+ :list_operation => ListOperationType::ADD_TO,
271
+ :list_key => LIST_KEY,
272
+ :strict => 'false',
273
+ :list_member_list => {
274
+ :lead_key => [
275
+ {
276
+ :key_type => 'EMAIL',
277
+ :key_value => EMAIL
278
+ }
279
+ ]
280
+ }
281
+ }),
282
+ 'ns1:paramsListOperation',
283
+ response_hash)
284
+
285
+ @client.add_to_list(LIST_KEY, EMAIL).should == response_hash
286
+ end
287
+
288
+ it "should have the correct body format on remove_from_list" do
289
+ response_hash = {} # TODO
290
+ expect_request(@savon_client,
291
+ @authentication_header,
292
+ equals_matcher({
293
+ :list_operation => ListOperationType::REMOVE_FROM,
294
+ :list_key => LIST_KEY,
295
+ :strict => 'false',
296
+ :list_member_list => {
297
+ :lead_key => [
298
+ {
299
+ :key_type => 'EMAIL',
300
+ :key_value => EMAIL
301
+ }
302
+ ]
303
+ }
304
+ }),
305
+ 'ns1:paramsListOperation',
306
+ response_hash)
307
+
308
+ @client.remove_from_list(LIST_KEY, EMAIL).should == response_hash
309
+ end
310
+
311
+ it "should have the correct body format on is_member_of_list?" do
312
+ response_hash = {} # TODO
313
+ expect_request(@savon_client,
314
+ @authentication_header,
315
+ equals_matcher({
316
+ :list_operation => ListOperationType::IS_MEMBER_OF,
317
+ :list_key => LIST_KEY,
318
+ :strict => 'false',
319
+ :list_member_list => {
320
+ :lead_key => [
321
+ {
322
+ :key_type => 'EMAIL',
323
+ :key_value => EMAIL
324
+ }
325
+ ]
326
+ }
327
+ }),
328
+ 'ns1:paramsListOperation',
329
+ response_hash)
330
+
331
+ @client.is_member_of_list?(LIST_KEY, EMAIL).should == response_hash
332
+ end
333
+ end
334
+ end
335
+
336
+ private
337
+
338
+ def equals_matcher(expected)
339
+ Proc.new { |actual|
340
+ actual.should == expected
341
+ }
342
+ end
343
+
344
+ def expect_request(savon_client, authentication_header, expected_body_matcher, expected_namespace, response_hash)
345
+ header_hash = stub('header_hash')
346
+ soap_response = stub('soap_response')
347
+ request_namespace = mock('namespace')
348
+ request_header = mock('request_header')
349
+ soap_request = mock('soap_request')
350
+ authentication_header.should_receive(:set_time)
351
+ authentication_header.should_receive(:to_hash).and_return(header_hash)
352
+ request_namespace.should_receive(:[]=).with("xmlns:ns1", "http://www.marketo.com/mktows/")
353
+ request_header.should_receive(:[]=).with("ns1:AuthenticationHeader", header_hash)
354
+ soap_request.should_receive(:namespaces).and_return(request_namespace)
355
+ soap_request.should_receive(:header).and_return(request_header)
356
+ soap_request.should_receive(:body=) do |actual_body|
357
+ expected_body_matcher.call(actual_body)
358
+ end
359
+ soap_response.should_receive(:to_hash).and_return(response_hash)
360
+ savon_client.should_receive(:request).with(expected_namespace).and_yield(soap_request).and_return(soap_response)
361
+ end
362
+ end
363
+
364
+ describe ListOperationType do
365
+ it 'should define the correct types' do
366
+ ListOperationType::ADD_TO.should == 'ADDTOLIST'
367
+ ListOperationType::IS_MEMBER_OF.should == 'ISMEMBEROFLIST'
368
+ ListOperationType::REMOVE_FROM.should == 'REMOVEFROMLIST'
369
+ end
370
+ end
371
+ end
372
+ end
@@ -0,0 +1,40 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ module Rapleaf
4
+ module Marketo
5
+ describe LeadKeyType do
6
+ it "should define the correct types" do
7
+ LeadKeyType::IDNUM.should == 'IDNUM'
8
+ LeadKeyType::COOKIE.should == 'COOKIE'
9
+ LeadKeyType::EMAIL.should == 'EMAIL'
10
+ LeadKeyType::LEADOWNEREMAIL.should == 'LEADOWNEREMAIL'
11
+ LeadKeyType::SFDCACCOUNTID.should == 'SFDCACCOUNTID'
12
+ LeadKeyType::SFDCCONTACTID.should == 'SFDCCONTACTID'
13
+ LeadKeyType::SFDCLEADID.should == 'SFDCLEADID'
14
+ LeadKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID'
15
+ LeadKeyType::SFDCOPPTYID.should == 'SFDCOPPTYID'
16
+ end
17
+ end
18
+
19
+ describe LeadKey do
20
+ it "should store type and value on construction" do
21
+ KEY_VALUE = 'a value'
22
+ KEY_TYPE = LeadKeyType::IDNUM
23
+ lead_key = LeadKey.new(KEY_TYPE, KEY_VALUE)
24
+ lead_key.key_type.should == KEY_TYPE
25
+ lead_key.key_value.should == KEY_VALUE
26
+ end
27
+
28
+ it "should to_hash correctly" do
29
+ KEY_VALUE = 'a value'
30
+ KEY_TYPE = LeadKeyType::IDNUM
31
+ lead_key = LeadKey.new(KEY_TYPE, KEY_VALUE)
32
+
33
+ lead_key.to_hash.should == {
34
+ :key_type => KEY_TYPE,
35
+ :key_value => KEY_VALUE
36
+ }
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,86 @@
1
+ require File.expand_path('../spec_helper', File.dirname(__FILE__))
2
+
3
+ module Rapleaf
4
+ module Marketo
5
+ EMAIL = 'some@email.com'
6
+ IDNUM = 93480938
7
+
8
+ describe LeadRecord do
9
+ it "should store the idnum" do
10
+ lead_record = LeadRecord.new(EMAIL, IDNUM)
11
+ lead_record.idnum.should == IDNUM
12
+ end
13
+
14
+ it "should store the email" do
15
+ LeadRecord.new(EMAIL, IDNUM).email.should == EMAIL
16
+ end
17
+
18
+ it "should implement == sensibly" do
19
+ lead_record1 = LeadRecord.new(EMAIL, IDNUM)
20
+ lead_record1.set_attribute('favourite color', 'red')
21
+ lead_record1.set_attribute('age', '100')
22
+
23
+ lead_record2 = LeadRecord.new(EMAIL, IDNUM)
24
+ lead_record2.set_attribute('favourite color', 'red')
25
+ lead_record2.set_attribute('age', '100')
26
+
27
+ lead_record1.should == lead_record2
28
+ end
29
+
30
+ it "should store when attributes are set" do
31
+ lead_record = LeadRecord.new(EMAIL, IDNUM)
32
+ lead_record.set_attribute('favourite color', 'red')
33
+ lead_record.get_attribute('favourite color').should == 'red'
34
+ end
35
+
36
+ it "should store when attributes are updated" do
37
+ lead_record = LeadRecord.new(EMAIL, IDNUM)
38
+ lead_record.set_attribute('favourite color', 'red')
39
+ lead_record.set_attribute('favourite color', 'green')
40
+ lead_record.get_attribute('favourite color').should == 'green'
41
+ end
42
+
43
+ it "should yield all attributes through each_attribute_pair" do
44
+ lead_record = LeadRecord.new(EMAIL, IDNUM)
45
+ lead_record.set_attribute('favourite color', 'red')
46
+ lead_record.set_attribute('favourite color', 'green')
47
+ lead_record.set_attribute('age', '99')
48
+
49
+ pairs = []
50
+ lead_record.each_attribute_pair do |attribute_name, attribute_value|
51
+ pairs << [attribute_name, attribute_value]
52
+ end
53
+
54
+ pairs.size.should == 3
55
+ pairs.should include(['favourite color', 'green'])
56
+ pairs.should include(['age', '99'])
57
+ pairs.should include(['Email', EMAIL])
58
+ end
59
+
60
+ it "should be instantiable from a savon hash" do
61
+ savon_hash = {
62
+ :email => EMAIL,
63
+ :foreign_sys_type => nil,
64
+ :lead_attribute_list => {
65
+ :attribute => [
66
+ { :attr_name => 'Company', :attr_type => 'string', :attr_value => 'Rapleaf'},
67
+ { :attr_name => 'FirstName', :attr_type => 'string', :attr_value => 'James'},
68
+ { :attr_name => 'LastName', :attr_type => 'string', :attr_value => 'O\'Brien'}
69
+ ]
70
+ },
71
+ :foreign_sys_person_id => nil,
72
+ :id => IDNUM
73
+ }
74
+
75
+ actual = LeadRecord.from_hash(savon_hash)
76
+
77
+ expected = LeadRecord.new(EMAIL, IDNUM)
78
+ expected.set_attribute('Company', 'Rapleaf')
79
+ expected.set_attribute('FirstName', 'James')
80
+ expected.set_attribute('LastName', 'O\'Brien')
81
+
82
+ actual.should == expected
83
+ end
84
+ end
85
+ end
86
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: njones-marketo
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 1
7
+ - 2
8
+ - 5
9
+ version: 1.2.5
10
+ platform: ruby
11
+ authors:
12
+ - James O'Brien (orginal author)
13
+ - Nika Jones
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-11-29 00:00:00 -06:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 2
30
+ - 3
31
+ - 0
32
+ version: 2.3.0
33
+ type: :development
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: savon
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ segments:
43
+ - 0
44
+ - 8
45
+ - 3
46
+ version: 0.8.3
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description: " A Fork of the Rapleaf marketo-gem. Once the updates in the gem are merged into that one, you should use that.\n\n Allows easy integration with marketo from ruby. You can synchronize leads (multiple leads too) and fetch them back by email. This is based on the SOAP wsdl file: http://app.marketo.com/soap/mktows/1_7?WSDL (by default, but you can change on the fly). More information at https://www.rapleaf.com/developers/marketo. And look to https://github.com/njones/marketo_gem/tree/njones-marketo for updates.\n"
50
+ email: njones@appcelerator.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - lib/marketo/authentication_header.rb
59
+ - lib/marketo/client.rb
60
+ - lib/marketo/enums.rb
61
+ - lib/marketo/lead_key.rb
62
+ - lib/marketo/lead_record.rb
63
+ - lib/njones-marketo.rb
64
+ has_rdoc: true
65
+ homepage: https://github.com/njones/marketo_gem
66
+ licenses: []
67
+
68
+ post_install_message:
69
+ rdoc_options:
70
+ - --title
71
+ - Marketo Client Gem
72
+ - --main
73
+ - Rapleaf::Marketo::Client
74
+ require_paths:
75
+ - - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.6
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: A client for using the marketo API
97
+ test_files:
98
+ - spec/marketo/authentication_header_spec.rb
99
+ - spec/marketo/client_spec.rb
100
+ - spec/marketo/lead_key_spec.rb
101
+ - spec/marketo/lead_record_spec.rb