From d7a9a253111d1926f3ee859dc16d18f7531f8668 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Tue, 16 Jul 2013 13:42:33 -0400 Subject: [PATCH 01/30] Create README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..c0fa889 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +marketo_gem +=========== +Fork of the original Rapleaf marketo gem to support updates to the Marketo API and fix some bugs. From 4778cb982c07bc9a3f5b3340e40b305c74fdff10 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Wed, 17 Jul 2013 14:27:28 -0400 Subject: [PATCH 02/30] remove Savon 1.x style configuration for compatbility with Savon 2.x --- lib/marketo.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/marketo.rb b/lib/marketo.rb index 01906f0..8d607b7 100644 --- a/lib/marketo.rb +++ b/lib/marketo.rb @@ -1,10 +1,6 @@ require 'rubygems' require 'savon' -Savon.configure do |config| - config.log = false # disable logging -end - require File.expand_path('marketo/client', File.dirname(__FILE__)) require File.expand_path('marketo/authentication_header', File.dirname(__FILE__)) require File.expand_path('marketo/enums', File.dirname(__FILE__)) From 25bb2059b663d773f20609d69f90ed2b3e06dbe5 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Wed, 17 Jul 2013 14:27:55 -0400 Subject: [PATCH 03/30] minor cleanup to make tests pass w/o warnings --- spec/marketo/authentication_header_spec.rb | 39 +++++++++++----------- spec/marketo/lead_key_spec.rb | 20 +++++------ spec/marketo/lead_record_spec.rb | 28 ++++++++-------- 3 files changed, 44 insertions(+), 43 deletions(-) diff --git a/spec/marketo/authentication_header_spec.rb b/spec/marketo/authentication_header_spec.rb index 6f0609f..ad6233e 100644 --- a/spec/marketo/authentication_header_spec.rb +++ b/spec/marketo/authentication_header_spec.rb @@ -1,44 +1,45 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) +require 'pry' module Rapleaf module Marketo - ACCESS_KEY = 'ACCESS_KEY' - SECRET_KEY = 'SECRET_KEY' + #from Marketo API docs + TEST_ACCESS_KEY = 'bigcorp1_461839624B16E06BA2D663' + TEST_SECRET_KEY = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' + TEST_DATE = DateTime.new(2010, 4, 9, 14, 4, 54, -7/24.0) + TEST_DATE_STRING = '2010-04-09T14:04:54-07:00' + TEST_SIGNATURE = 'ffbff4d4bef354807481e66dc7540f7890523a87' describe AuthenticationHeader do it "should set mktowsUserId to access key" do - header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) - header.get_mktows_user_id.should == ACCESS_KEY + header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + header.get_mktows_user_id.should == TEST_ACCESS_KEY end it "should set requestSignature" do - header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) + header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + header.get_request_signature.should_not be_nil header.get_request_signature.should_not == '' end it "should set requestTimestamp in correct format" do - header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) - time = DateTime.new(1998, 1, 17, 20, 15, 1) - header.set_time(time) - header.get_request_timestamp().should == '1998-01-17T20:15:01+00:00' + header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + header.set_time(TEST_DATE) + + header.get_request_timestamp().should == TEST_DATE_STRING end it "should calculate encrypted signature" do - # I got this example of the marketo API docs - - access_key = 'bigcorp1_461839624B16E06BA2D663' - secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' - - header = Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key) - header.set_time(DateTime.new(2010, 4, 9, 14, 4, 55, -7/24.0)) + header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + header.set_time(TEST_DATE) - header.get_request_timestamp.should == '2010-04-09T14:04:54-07:00' - header.get_request_signature.should == 'ffbff4d4bef354807481e66dc7540f7890523a87' + header.get_request_timestamp.should == TEST_DATE_STRING + header.get_request_signature.should == TEST_SIGNATURE end it "should cope if no date is given" do - header = Rapleaf::Marketo::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) + header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) expected = DateTime.now actual = DateTime.parse(header.get_request_timestamp) diff --git a/spec/marketo/lead_key_spec.rb b/spec/marketo/lead_key_spec.rb index ba2f146..5c9f18b 100644 --- a/spec/marketo/lead_key_spec.rb +++ b/spec/marketo/lead_key_spec.rb @@ -16,23 +16,23 @@ module Marketo end end + TEST_KEY_VALUE = 'a value' + TEST_KEY_TYPE = LeadKeyType::IDNUM + describe LeadKey do it "should store type and value on construction" do - KEY_VALUE = 'a value' - KEY_TYPE = LeadKeyType::IDNUM - lead_key = LeadKey.new(KEY_TYPE, KEY_VALUE) - lead_key.key_type.should == KEY_TYPE - lead_key.key_value.should == KEY_VALUE + + lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + lead_key.key_type.should == TEST_KEY_VALUE + lead_key.key_value.should == TEST_KEY_VALUE end it "should to_hash correctly" do - KEY_VALUE = 'a value' - KEY_TYPE = LeadKeyType::IDNUM - lead_key = LeadKey.new(KEY_TYPE, KEY_VALUE) + lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) lead_key.to_hash.should == { - :key_type => KEY_TYPE, - :key_value => KEY_VALUE + :key_type => TEST_KEY_VALUE, + :key_value => TEST_KEY_VALUE } end end diff --git a/spec/marketo/lead_record_spec.rb b/spec/marketo/lead_record_spec.rb index c616fed..550756b 100644 --- a/spec/marketo/lead_record_spec.rb +++ b/spec/marketo/lead_record_spec.rb @@ -2,25 +2,25 @@ module Rapleaf module Marketo - EMAIL = 'some@email.com' - IDNUM = 93480938 + TEST_EMAIL = 'some@email.com' + TEST_IDNUM = 93480938 describe LeadRecord do it "should store the idnum" do - lead_record = LeadRecord.new(EMAIL, IDNUM) - lead_record.idnum.should == IDNUM + lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + lead_record.idnum.should == TEST_IDNUM end it "should store the email" do - LeadRecord.new(EMAIL, IDNUM).email.should == EMAIL + LeadRecord.new(TEST_EMAIL, TEST_IDNUM).email.should == TEST_EMAIL end it "should implement == sensibly" do - lead_record1 = LeadRecord.new(EMAIL, IDNUM) + lead_record1 = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) lead_record1.set_attribute('favourite color', 'red') lead_record1.set_attribute('age', '100') - lead_record2 = LeadRecord.new(EMAIL, IDNUM) + lead_record2 = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) lead_record2.set_attribute('favourite color', 'red') lead_record2.set_attribute('age', '100') @@ -28,20 +28,20 @@ module Marketo end it "should store when attributes are set" do - lead_record = LeadRecord.new(EMAIL, IDNUM) + lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) lead_record.set_attribute('favourite color', 'red') lead_record.get_attribute('favourite color').should == 'red' end it "should store when attributes are updated" do - lead_record = LeadRecord.new(EMAIL, IDNUM) + lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) lead_record.set_attribute('favourite color', 'red') lead_record.set_attribute('favourite color', 'green') lead_record.get_attribute('favourite color').should == 'green' end it "should yield all attributes through each_attribute_pair" do - lead_record = LeadRecord.new(EMAIL, IDNUM) + lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) lead_record.set_attribute('favourite color', 'red') lead_record.set_attribute('favourite color', 'green') lead_record.set_attribute('age', '99') @@ -54,12 +54,12 @@ module Marketo pairs.size.should == 3 pairs.should include(['favourite color', 'green']) pairs.should include(['age', '99']) - pairs.should include(['Email', EMAIL]) + pairs.should include(['Email', TEST_EMAIL]) end it "should be instantiable from a savon hash" do savon_hash = { - :email => EMAIL, + :email => TEST_EMAIL, :foreign_sys_type => nil, :lead_attribute_list => { :attribute => [ @@ -69,12 +69,12 @@ module Marketo ] }, :foreign_sys_person_id => nil, - :id => IDNUM + :id => TEST_IDNUM } actual = LeadRecord.from_hash(savon_hash) - expected = LeadRecord.new(EMAIL, IDNUM) + expected = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) expected.set_attribute('Company', 'Rapleaf') expected.set_attribute('FirstName', 'James') expected.set_attribute('LastName', 'O\'Brien') From 8e1ad94537ab43d0d56ff0457464a467664082b5 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Wed, 17 Jul 2013 14:32:57 -0400 Subject: [PATCH 04/30] update gemspec after forking --- marketo.gemspec | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/marketo.gemspec b/marketo.gemspec index ebf5fdc..61d2342 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -3,21 +3,18 @@ Gem::Specification.new do |gem| gem.summary = "A client for the marketo API" gem.description = <<-EOF Allows easy integration with marketo from ruby. You can synchronize leads and fetch them back by email. - By default this is configured for the SOAP wsdl file: http://app.marketo.com/soap/mktows/1_4?WSDL but this is - configurable when you construct the client, e.g. - client = Rapleaf::Marketo.new_client(, , (api_subdomain = 'na-i'), (api_version = '1_5'), (document_version = '1_4')) - More information at https://www.rapleaf.com/developers/marketo. + Forked from the Rapleaf Marketo API Gem, with updates for Marketo API 2.x and Savon 2.x. EOF - gem.email = "james@rapleaf.com" - gem.authors = ["James O'Brien"] - gem.homepage = "https://www.rapleaf.com/developers/marketo" + gem.email = "joey@grabcad.com" + gem.authors = ["Joseph George"] + gem.homepage = "https://github.com/jgeorge-gc/marketo_gem" gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "1.4.0" + gem.version = "1.4.1" gem.has_rdoc = true - gem.rdoc_options << '--title' << 'Marketo Client Gem' << '--main' << 'Rapleaf::Marketo::Client' + gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' gem.add_development_dependency('rspec', '>= 2.3.0') - gem.add_dependency('savon', '>= 0.8.3') + gem.add_dependency('savon', '>= ') end From 576c318bd227064e087ad1e2405dd8a91734911c Mon Sep 17 00:00:00 2001 From: Joseph George Date: Wed, 17 Jul 2013 14:34:43 -0400 Subject: [PATCH 05/30] update gemspec after forking --- marketo.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marketo.gemspec b/marketo.gemspec index 61d2342..20c6291 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -16,5 +16,5 @@ Gem::Specification.new do |gem| gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' gem.add_development_dependency('rspec', '>= 2.3.0') - gem.add_dependency('savon', '>= ') + gem.add_dependency('savon', '>= 2.0') end From 4d090154a3fae1f14d9bca85a5f85429de44b277 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Thu, 18 Jul 2013 00:26:48 -0400 Subject: [PATCH 06/30] update gemspec, version --- marketo.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/marketo.gemspec b/marketo.gemspec index 20c6291..3176467 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,10 +11,10 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "1.4.1" + gem.version = "0.0.2" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' gem.add_development_dependency('rspec', '>= 2.3.0') - gem.add_dependency('savon', '>= 2.0') + gem.add_dependency('savon', '~> 2.2') end From bb7a843dbd7fdebd3f30c52d4d3a3ed7d0939042 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Thu, 18 Jul 2013 00:27:16 -0400 Subject: [PATCH 07/30] use generic company name in spec --- spec/marketo/client_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/marketo/client_spec.rb b/spec/marketo/client_spec.rb index ed9e0cd..dc64a0e 100644 --- a/spec/marketo/client_spec.rb +++ b/spec/marketo/client_spec.rb @@ -8,7 +8,7 @@ module Marketo IDNUM = 29 FIRST = 'Joe' LAST = 'Smith' - COMPANY = 'Rapleaf' + COMPANY = 'ABC Enterprises' MOBILE = '415 123 456' API_KEY = 'API123KEY' From a166e423d2d0d34d05ffe491bd4e30664cbe1423 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Thu, 18 Jul 2013 00:30:55 -0400 Subject: [PATCH 08/30] remove pry from spec --- spec/marketo/authentication_header_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/marketo/authentication_header_spec.rb b/spec/marketo/authentication_header_spec.rb index ad6233e..e4655cd 100644 --- a/spec/marketo/authentication_header_spec.rb +++ b/spec/marketo/authentication_header_spec.rb @@ -1,5 +1,4 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) -require 'pry' module Rapleaf module Marketo @@ -18,7 +17,7 @@ module Marketo it "should set requestSignature" do header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) - + header.get_request_signature.should_not be_nil header.get_request_signature.should_not == '' end From b262fe8e1c24a939bee8e1b0fc3f502897719bc0 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Thu, 18 Jul 2013 00:31:40 -0400 Subject: [PATCH 09/30] handle leads with no lead attributes set --- lib/marketo/lead_record.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/marketo/lead_record.rb b/lib/marketo/lead_record.rb index abafa59..5c23dc4 100644 --- a/lib/marketo/lead_record.rb +++ b/lib/marketo/lead_record.rb @@ -9,10 +9,13 @@ def initialize(email, idnum = nil) end # hydrates an instance from a savon hash returned form the marketo API - def self.from_hash(savon_hash) - lead_record = LeadRecord.new(savon_hash[:email], savon_hash[:id].to_i) - savon_hash[:lead_attribute_list][:attribute].each do |attribute| - lead_record.set_attribute(attribute[:attr_name], attribute[:attr_value]) + def self.from_hash(lead_hash) + lead_record = LeadRecord.new(lead_hash[:email], lead_hash[:id].to_i) + lead_attributes = lead_hash[:lead_attribute_list] + if (lead_attributes) + lead_hash[:lead_attribute_list][:attribute].each do |attribute| + lead_record.set_attribute(attribute[:attr_name], attribute[:attr_value]) + end end lead_record end From 1acff5abe931f68993a47f428b053d527507f776 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Thu, 18 Jul 2013 00:33:12 -0400 Subject: [PATCH 10/30] Update get_lead for new Savon API; broke lots of rspec; removed old constructor because it used deprecated means of creating/configuring Savon --- lib/marketo/client.rb | 46 +++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index a973031..f35e04c 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -1,18 +1,19 @@ require File.expand_path('authentication_header', File.dirname(__FILE__)) +require 'pry' module Rapleaf module Marketo - def self.new_client(access_key, secret_key, api_subdomain = 'na-i', api_version = '1_5', document_version = '1_4') - client = Savon::Client.new do - wsdl.endpoint = "https://#{api_subdomain}.marketo.com/soap/mktows/#{api_version}" - wsdl.document = "http://app.marketo.com/soap/mktows/#{document_version}?WSDL" - http.read_timeout = 90 - http.open_timeout = 90 - http.headers = {"Connection" => "Keep-Alive"} - end - - Client.new(client, Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key)) - end + # def self.new_client(access_key, secret_key, api_subdomain = 'na-i', api_version = '1_5', document_version = '1_4') + # client = Savon::Client.new do + # wsdl.endpoint = "https://#{api_subdomain}.marketo.com/soap/mktows/#{api_version}" + # wsdl.document = "http://app.marketo.com/soap/mktows/#{document_version}?WSDL" + # http.read_timeout = 90 + # http.open_timeout = 90 + # http.headers = {"Connection" => "Keep-Alive"} + # end + + # Client.new(client, Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key)) + # end # = The client for talking to marketo # based on the SOAP wsdl file: http://app.marketo.com/soap/mktows/1_4?WSDL @@ -176,26 +177,25 @@ def list_operation(list_key, list_operation_type, email) def get_lead(lead_key) begin - response = send_request("ns1:paramsGetLead", {:lead_key => lead_key.to_hash}) - return LeadRecord.from_hash(response[:success_get_lead][:result][:lead_record_list][:lead_record]) + response = send_request(:get_lead, {:lead_key => lead_key.to_hash}) + LeadRecord.from_hash(response[:success_get_lead][:result][:lead_record_list][:lead_record]) rescue Exception => e @logger.log(e) if @logger - return nil + nil end end - def send_request(namespace, body) + def send_request(operation, body) @header.set_time(DateTime.now) - response = request(namespace, body, @header.to_hash) - response.to_hash + response = request(operation, body, @header.to_hash) + response.body end - def request(namespace, body, header) - @client.request namespace do |soap| - soap.namespaces["xmlns:ns1"] = "http://www.marketo.com/mktows/" - soap.body = body - soap.header["ns1:AuthenticationHeader"] = header - end + def request(operation, body, header) #returns a Savon 2 response + auth_header = {"tns:AuthenticationHeader" => header.to_hash} + # set the marketo-specific authentication header with authentication SHA1 + @client.globals.soap_header(auth_header) + @client.call(operation, message: body) end end end From 822f42c875a4e2122f52f0db42562ecd673cac91 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Thu, 18 Jul 2013 00:34:48 -0400 Subject: [PATCH 11/30] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c0fa889..5dd8c7e 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ marketo_gem =========== Fork of the original Rapleaf marketo gem to support updates to the Marketo API and fix some bugs. + +This is still in experimental stages - use at your own risk until it becomes more stable! From f6137ebe02cf6e3a5a49d93fa8db00a39376949f Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 11:40:42 -0400 Subject: [PATCH 12/30] - Rewrite API for sanity and concision - remove list operations since they are broken - change module names since API is new - rspec still broken - documentation still out of date - remove unecessary methods --- lib/marketo/authentication_header.rb | 31 ++--- lib/marketo/client.rb | 192 +++++++++++++++++---------- lib/marketo/enums.rb | 2 +- lib/marketo/lead_key.rb | 2 +- lib/marketo/lead_record.rb | 63 ++++++--- 5 files changed, 173 insertions(+), 117 deletions(-) diff --git a/lib/marketo/authentication_header.rb b/lib/marketo/authentication_header.rb index 9a81087..cc05922 100644 --- a/lib/marketo/authentication_header.rb +++ b/lib/marketo/authentication_header.rb @@ -1,7 +1,7 @@ -module Rapleaf +module Grabcad module Marketo # This class exists only to encapsulate the authentication header part of a soap request to marketo - # Marketo requires a somewhat complex calculation of an encrypted signature and so it seemed sensible to pull this code out here + # It contains a SHA1-based MAC based on a timestamp class AuthenticationHeader DIGEST = OpenSSL::Digest::Digest.new('sha1') @@ -12,36 +12,25 @@ def initialize(access_key, secret_key, time = DateTime.now) end public - # time should be a DateTime instance def set_time(time) @time = time end - def get_mktows_user_id - @access_key - end - - def get_request_signature - calculate_signature - end - - def get_request_timestamp - @time.to_s - end - def to_hash { - "mktowsUserId" => get_mktows_user_id, - "requestSignature" => get_request_signature, - "requestTimestamp" => get_request_timestamp + "mktowsUserId" => @access_key, + "requestSignature" => calculate_signature, + "requestTimestamp" => request_timestamp } end private + def request_timestamp + @time.to_s + end + def calculate_signature - request_timestamp = get_request_timestamp - string_to_encrypt = request_timestamp + @access_key - OpenSSL::HMAC.hexdigest(DIGEST, @secret_key, string_to_encrypt) + OpenSSL::HMAC.hexdigest(DIGEST, @secret_key, request_timestamp + @access_key) end end end diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index f35e04c..e440ed6 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -1,21 +1,25 @@ require File.expand_path('authentication_header', File.dirname(__FILE__)) require 'pry' -module Rapleaf +module Grabcad module Marketo - # def self.new_client(access_key, secret_key, api_subdomain = 'na-i', api_version = '1_5', document_version = '1_4') - # client = Savon::Client.new do - # wsdl.endpoint = "https://#{api_subdomain}.marketo.com/soap/mktows/#{api_version}" - # wsdl.document = "http://app.marketo.com/soap/mktows/#{document_version}?WSDL" - # http.read_timeout = 90 - # http.open_timeout = 90 - # http.headers = {"Connection" => "Keep-Alive"} - # end - - # Client.new(client, Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key)) - # end - - # = The client for talking to marketo + def self.create_client (access_key, secret_key, endpoint_uri, wsdl_uri = "http://app.marketo.com/soap/mktows/2_1?WSDL") + savon_client = Savon.client do + endpoint endpoint_uri + wsdl wsdl_uri + read_timeout 90 + open_timeout 90 + headers ({ "Connection" => "Keep-Alive" }) + log_level :debug + log false + pretty_print_xml true + end + + Client.new(savon_client, Grabcad::Marketo::AuthenticationHeader.new(access_key, secret_key)) + end + + + # = The client for talking to marketo - WARNING: docs are old and need to be updated # based on the SOAP wsdl file: http://app.marketo.com/soap/mktows/1_4?WSDL # # Usage: @@ -61,12 +65,18 @@ class Client def initialize(savon_client, authentication_header) @client = savon_client @header = authentication_header + if Rails + logger=Rails.logger + else + logger= Logger.new(STDOUT) + end + @client.globals.log true end public - def get_lead_by_idnum(idnum) - get_lead(LeadKey.new(LeadKeyType::IDNUM, idnum)) + def get_lead_by_lead_id(lead_id) + get_lead(LeadKey.new(LeadKeyType::IDNUM, lead_id)) end @@ -74,8 +84,32 @@ def get_lead_by_email(email) get_lead(LeadKey.new(LeadKeyType::EMAIL, email)) end - def set_logger(logger) + def logger=(logger) #Specify a logger compatible with ruby logger @logger = logger + @client.globals.logger logger + HTTPI.logger = logger + end + + def log_level=(level) + if (level.kind_of? (Fixnum)) + @logger.level = level + symbolic_level = case level + when Logger::DEBUG + :debug + when Logger::INFO + :info + when Logger::WARN + :warn + when Logger::ERROR + :error + else + :fatal + end + @client.globals.log_level symbolic_level + HTTPI.log_level = symbolic_level + else + false + end end # create (if new) or update (if existing) a lead @@ -84,42 +118,42 @@ def set_logger(logger) # * first - first name of lead # * last - surname/last name of lead # * company - company the lead is associated with - # * mobile - mobile/cell phone number # # returns the LeadRecord instance on success otherwise nil - def sync_lead(email, first, last, company, mobile) - lead_record = LeadRecord.new(email) + def sync_lead(email, first, last, company, lead_id = nil) + lead_record = LeadRecord.new(self, email, lead_id) lead_record.set_attribute('FirstName', first) lead_record.set_attribute('LastName', last) lead_record.set_attribute('Email', email) lead_record.set_attribute('Company', company) - lead_record.set_attribute('MobilePhone', mobile) - sync_lead_record(lead_record) + lead_record.sync end - def sync_lead_record(lead_record) + def sync_lead_record_by_email(lead_record) + raise 'Email not set - Cannot sync lead record without email' if lead_record.email.nil? + begin attributes = [] lead_record.each_attribute_pair do |name, value| attributes << {:attr_name => name, :attr_type => 'string', :attr_value => value} end - response = send_request("ns1:paramsSyncLead", { - :return_lead => true, - :lead_record => - {:email => lead_record.email, - :lead_attribute_list => { - :attribute => attributes}}}) - return LeadRecord.from_hash(response[:success_sync_lead][:result][:lead_record]) + response = send_request(:sync_lead, { + :return_lead => true, + :lead_record => { + :email => lead_record.email, + :lead_attribute_list => { :attribute => attributes } + } + }) + response[:success_sync_lead][:result][:lead_record] rescue Exception => e - @logger.log(e) if @logger - return nil + log_exception e + nil end end - def sync_lead_record_on_id(lead_record) - idnum = lead_record.idnum - raise 'lead record id not set' if idnum.nil? + def sync_lead_record_by_id(lead_record) + raise 'ID not set - Cannot sync lead record without ID' if lead_record.id.nil? begin attributes = [] @@ -127,60 +161,71 @@ def sync_lead_record_on_id(lead_record) attributes << {:attr_name => name, :attr_type => 'string', :attr_value => value} end - attributes << {:attr_name => 'Id', :attr_type => 'string', :attr_value => idnum.to_s} + attributes << {:attr_name => 'Id', :attr_type => 'string', :attr_value => lead_record.id.to_s} - response = send_request("ns1:paramsSyncLead", { - :return_lead => true, - :lead_record => - { - :lead_attribute_list => { :attribute => attributes}, - :id => idnum - }}) - return LeadRecord.from_hash(response[:success_sync_lead][:result][:lead_record]) + response = send_request(:sync_lead, { + :return_lead => true, + :lead_record => { + :lead_attribute_list => { :attribute => attributes }, + :id => lead_record.id + } + }) + response[:success_sync_lead][:result][:lead_record] rescue Exception => e - @logger.log(e) if @logger - return nil + log_exception e + nil end end - def add_to_list(list_key, email) - list_operation(list_key, ListOperationType::ADD_TO, email) - end + #Remove list operations until their implementation can be reviewed + # def add_to_list(list_key, email) + # list_operation(list_key, ListOperationType::ADD_TO, email) + # end + + # def remove_from_list(list_key, email) + # list_operation(list_key, ListOperationType::REMOVE_FROM, email) + # end + + # def is_member_of_list?(list_key, email) + # list_operation(list_key, ListOperationType::IS_MEMBER_OF, email) + # end - def remove_from_list(list_key, email) - list_operation(list_key, ListOperationType::REMOVE_FROM, email) + def enable_soap_debugging + @client.pretty_print_xml = true end - def is_member_of_list?(list_key, email) - list_operation(list_key, ListOperationType::IS_MEMBER_OF, email) + def log_exception(exp) + @logger.warn(exp) + @logger.warn(exp.backtrace) end private - def list_operation(list_key, list_operation_type, email) - begin - response = send_request("ns1:paramsListOperation", { - :list_operation => list_operation_type, - :list_key => list_key, - :strict => 'false', - :list_member_list => { - :lead_key => [ - {:key_type => 'EMAIL', :key_value => email} - ] - } - }) - return response - rescue Exception => e - @logger.log(e) if @logger - return nil - end - end + #Remove list operations until their implementation can be reviewed + #def list_operation(list_key, list_operation_type, email) + # begin + # response = send_request(:list_operation, { + # :list_operation => list_operation_type, + # :list_key => list_key, + # :strict => 'false', + # :list_member_list => { + # :lead_key => [ + # {:key_type => 'EMAIL', :key_value => email} + # ] + # } + # }) + # return response + # rescue Exception => e + # log_exception e + # return nil + # end + # end def get_lead(lead_key) begin response = send_request(:get_lead, {:lead_key => lead_key.to_hash}) - LeadRecord.from_hash(response[:success_get_lead][:result][:lead_record_list][:lead_record]) + LeadRecord.from_hash(self, response[:success_get_lead][:result][:lead_record_list][:lead_record]) rescue Exception => e - @logger.log(e) if @logger + log_exception e nil end end @@ -197,6 +242,7 @@ def request(operation, body, header) #returns a Savon 2 response @client.globals.soap_header(auth_header) @client.call(operation, message: body) end + end end end \ No newline at end of file diff --git a/lib/marketo/enums.rb b/lib/marketo/enums.rb index 6ee3d25..1a2e613 100644 --- a/lib/marketo/enums.rb +++ b/lib/marketo/enums.rb @@ -1,4 +1,4 @@ -module Rapleaf +module Grabcad module Marketo # Types of operations you can do on a marketo list module ListOperationType diff --git a/lib/marketo/lead_key.rb b/lib/marketo/lead_key.rb index 09c1da1..ec38132 100644 --- a/lib/marketo/lead_key.rb +++ b/lib/marketo/lead_key.rb @@ -1,4 +1,4 @@ -module Rapleaf +module Grabcad module Marketo # Encapsulates a key used to look up or describe a specific marketo lead. class LeadKey diff --git a/lib/marketo/lead_record.rb b/lib/marketo/lead_record.rb index 5c23dc4..9d74c4f 100644 --- a/lib/marketo/lead_record.rb +++ b/lib/marketo/lead_record.rb @@ -1,28 +1,30 @@ -module Rapleaf +module Grabcad module Marketo # Represents a record of the data known about a lead within marketo class LeadRecord - def initialize(email, idnum = nil) - @idnum = idnum + attr_reader :id, :attributes + + def initialize(client, email, lead_id = nil, attrs = nil) + @client = client + @id = lead_id @attributes = {} + if (attrs) + populate_from attrs + end set_attribute('Email', email) end - # hydrates an instance from a savon hash returned form the marketo API - def self.from_hash(lead_hash) - lead_record = LeadRecord.new(lead_hash[:email], lead_hash[:id].to_i) - lead_attributes = lead_hash[:lead_attribute_list] - if (lead_attributes) - lead_hash[:lead_attribute_list][:attribute].each do |attribute| - lead_record.set_attribute(attribute[:attr_name], attribute[:attr_value]) - end - end - lead_record + # creates and populates an instance from a savon hash returned form the marketo API + def self.from_hash(client, lead_hash) + lead_record = LeadRecord.new(client, lead_hash[:email], lead_hash[:id].to_i, lead_hash) end - # get the record idnum - def idnum - @idnum + def sync + if @id + populate_from @client.sync_lead_record_by_id(self) + else + populate_from @client.sync_lead_record_by_email(self) + end end # get the record email @@ -30,10 +32,6 @@ def email get_attribute('Email') end - def attributes - @attributes - end - # update the value of the named attribute def set_attribute(name, value) @attributes[name] = value @@ -53,7 +51,30 @@ def each_attribute_pair(&block) def ==(other) @attributes == other.attributes && - @idnum == other.idnum + @id == other.id + end + + private + + def populate_from(lead_hash) + begin + @id = lead_hash[:id].to_i + lead_attributes = lead_hash[:lead_attribute_list] + if lead_attributes # if no attributes set + attrs = lead_attributes[:attribute] + if attrs.kind_of?(Array) # if only one attribute set, service doesn't return an array + attrs.each do |attribute| + set_attribute(attribute[:attr_name], attribute[:attr_value]) + end + else + set_attribute(attrs[:attr_name], attrs[:attr_value]) + end + end + self + rescue Exception => e + log_exception e + nil + end end end end From d9d790b18b5f81e1b492b97bb386498980543916 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 11:49:49 -0400 Subject: [PATCH 13/30] update version to 0.0.3 --- marketo.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marketo.gemspec b/marketo.gemspec index 3176467..85df71f 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.2" + gem.version = "0.0.3" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' From a53f8bc3a71c79e0a6b856ab6a88d479f2fc98e8 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 16:16:30 -0400 Subject: [PATCH 14/30] update to 0.0.4, change sync_lead to be more descriptive --- lib/marketo/client.rb | 2 +- marketo.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index e440ed6..2f85648 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -120,7 +120,7 @@ def log_level=(level) # * company - company the lead is associated with # # returns the LeadRecord instance on success otherwise nil - def sync_lead(email, first, last, company, lead_id = nil) + def upsert_lead(email, first, last, company, lead_id = nil) lead_record = LeadRecord.new(self, email, lead_id) lead_record.set_attribute('FirstName', first) lead_record.set_attribute('LastName', last) diff --git a/marketo.gemspec b/marketo.gemspec index 85df71f..733f23f 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.3" + gem.version = "0.0.4" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' From 9854a43dc5609903e007df71f0381d7bb7fe8d66 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 16:47:20 -0400 Subject: [PATCH 15/30] 0.0.5: default log fixes --- lib/marketo/client.rb | 10 ++++++---- marketo.gemspec | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index 2f85648..0891ba7 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -65,10 +65,10 @@ class Client def initialize(savon_client, authentication_header) @client = savon_client @header = authentication_header - if Rails + if Rails && Rails.logger logger=Rails.logger else - logger= Logger.new(STDOUT) + logger=Logger.new(STDOUT) end @client.globals.log true end @@ -195,8 +195,10 @@ def enable_soap_debugging end def log_exception(exp) - @logger.warn(exp) - @logger.warn(exp.backtrace) + if @logger + @logger.warn(exp) + @logger.warn(exp.backtrace) + end end private diff --git a/marketo.gemspec b/marketo.gemspec index 733f23f..97dbb37 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.4" + gem.version = "0.0.5" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' From 940e7870119124e5b1c9a614b70bf929871ea1e3 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 17:16:29 -0400 Subject: [PATCH 16/30] fix classname reference in gemspec --- marketo.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marketo.gemspec b/marketo.gemspec index 97dbb37..2a015b8 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -13,7 +13,7 @@ Gem::Specification.new do |gem| gem.test_files = Dir['spec/**/*_spec.rb'] gem.version = "0.0.5" gem.has_rdoc = true - gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Rapleaf::Marketo::Client' + gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Grabcad::Marketo::Client' gem.add_development_dependency('rspec', '>= 2.3.0') gem.add_dependency('savon', '~> 2.2') From 9811807b01d4574b37a7a17a7df796ddb50dfe09 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 18:17:36 -0400 Subject: [PATCH 17/30] logger fixes --- lib/marketo/client.rb | 3 +-- lib/marketo/lead_record.rb | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index 0891ba7..abad5bf 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -92,7 +92,7 @@ def logger=(logger) #Specify a logger compatible with ruby logger def log_level=(level) if (level.kind_of? (Fixnum)) - @logger.level = level + @logger.level = level if @logger symbolic_level = case level when Logger::DEBUG :debug @@ -244,7 +244,6 @@ def request(operation, body, header) #returns a Savon 2 response @client.globals.soap_header(auth_header) @client.call(operation, message: body) end - end end end \ No newline at end of file diff --git a/lib/marketo/lead_record.rb b/lib/marketo/lead_record.rb index 9d74c4f..8577886 100644 --- a/lib/marketo/lead_record.rb +++ b/lib/marketo/lead_record.rb @@ -72,7 +72,7 @@ def populate_from(lead_hash) end self rescue Exception => e - log_exception e + client.log_exception e nil end end From df319d3d004e80352c4ae0be73cb686478f5f2bf Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 18:19:32 -0400 Subject: [PATCH 18/30] update version --- marketo.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marketo.gemspec b/marketo.gemspec index 2a015b8..07e812f 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.5" + gem.version = "0.0.6" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Grabcad::Marketo::Client' From 30a8fad19f8ea3e9f38d33cd70e26166517edc3a Mon Sep 17 00:00:00 2001 From: Joseph George Date: Fri, 19 Jul 2013 19:43:03 -0400 Subject: [PATCH 19/30] remove pry --- lib/marketo/client.rb | 1 - marketo.gemspec | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index abad5bf..a9a2129 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -1,5 +1,4 @@ require File.expand_path('authentication_header', File.dirname(__FILE__)) -require 'pry' module Grabcad module Marketo diff --git a/marketo.gemspec b/marketo.gemspec index 07e812f..e7e6baf 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.6" + gem.version = "0.0.7" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Grabcad::Marketo::Client' From 25c7425281ef97396e2068aa99ea57d7069aadc9 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Sat, 20 Jul 2013 02:15:40 -0400 Subject: [PATCH 20/30] rspec tests run but don't all succeed; lead_key and authentication_header pass --- spec/marketo/authentication_header_spec.rb | 135 ++++++++++++++------- spec/marketo/client_spec.rb | 2 +- spec/marketo/lead_key_spec.rb | 2 +- spec/marketo/lead_record_spec.rb | 2 +- 4 files changed, 95 insertions(+), 46 deletions(-) diff --git a/spec/marketo/authentication_header_spec.rb b/spec/marketo/authentication_header_spec.rb index e4655cd..132f6cc 100644 --- a/spec/marketo/authentication_header_spec.rb +++ b/spec/marketo/authentication_header_spec.rb @@ -1,66 +1,115 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) -module Rapleaf +module Grabcad module Marketo - #from Marketo API docs + #Test values from Marketo API docs TEST_ACCESS_KEY = 'bigcorp1_461839624B16E06BA2D663' TEST_SECRET_KEY = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' TEST_DATE = DateTime.new(2010, 4, 9, 14, 4, 54, -7/24.0) TEST_DATE_STRING = '2010-04-09T14:04:54-07:00' TEST_SIGNATURE = 'ffbff4d4bef354807481e66dc7540f7890523a87' + + # hash keys with names from Marketo API + SIGNATURE_KEY = 'requestSignature' + USER_KEY = 'mktowsUserId' + TIMESTAMP_KEY = 'requestTimestamp' + + # expected structure of hash output + TEST_HASH = { + USER_KEY => TEST_ACCESS_KEY, + SIGNATURE_KEY => TEST_SIGNATURE, + TIMESTAMP_KEY => TEST_DATE_STRING, + } + describe AuthenticationHeader do - it "should set mktowsUserId to access key" do - header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) - header.get_mktows_user_id.should == TEST_ACCESS_KEY + describe "tests with 3-argument constructor" do + before(:each) do + @auth_header = Grabcad::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY, TEST_DATE) + @auth_hash = @auth_header.to_hash + end + + it "to_hash should create the proper hash for known inputs" do + @auth_hash.should == TEST_HASH + end + it "hash should change if time changes" do + @auth_header.set_time(DateTime.now) + hash = @auth_header.to_hash + @auth_hash[USER_KEY].should == hash[USER_KEY] + @auth_hash[SIGNATURE_KEY].should_not == hash[SIGNATURE_KEY] + @auth_hash[TIMESTAMP_KEY].should_not == hash[TIMESTAMP_KEY] + end end + + describe "tests with 2-argument constructor" do + before(:each) do + @auth_header = Grabcad::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + @auth_hash = @auth_header.to_hash + end + it "should format the time correctly" do + @auth_header.set_time(TEST_DATE) + @auth_header.to_hash[TIMESTAMP_KEY].should == TEST_DATE_STRING + end + it "should create a hash with the default time" do + @auth_hash[USER_KEY].should be_a(String) + @auth_hash[SIGNATURE_KEY].should be_a(String) + @auth_hash[TIMESTAMP_KEY].should be_a(String) + end + end + end + end +end + # it "should set mktowsUserId to access key" do + # header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + # header.get_mktows_user_id.should == TEST_ACCESS_KEY + # end - it "should set requestSignature" do - header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + # it "should set requestSignature" do + # header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) - header.get_request_signature.should_not be_nil - header.get_request_signature.should_not == '' - end + # header.get_request_signature.should_not be_nil + # header.get_request_signature.should_not == '' + # end - it "should set requestTimestamp in correct format" do - header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) - header.set_time(TEST_DATE) + # it "should set requestTimestamp in correct format" do + # header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + # header.set_time(TEST_DATE) - header.get_request_timestamp().should == TEST_DATE_STRING - end + # header.get_request_timestamp().should == TEST_DATE_STRING + # end - it "should calculate encrypted signature" do - header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) - header.set_time(TEST_DATE) + # it "should calculate encrypted signature" do + # header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + # header.set_time(TEST_DATE) - header.get_request_timestamp.should == TEST_DATE_STRING - header.get_request_signature.should == TEST_SIGNATURE - end + # header.get_request_timestamp.should == TEST_DATE_STRING + # header.get_request_signature.should == TEST_SIGNATURE + # end - it "should cope if no date is given" do - header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) - expected = DateTime.now - actual = DateTime.parse(header.get_request_timestamp) + # it "should cope if no date is given" do + # header = Rapleaf::Marketo::AuthenticationHeader.new(TEST_ACCESS_KEY, TEST_SECRET_KEY) + # expected = DateTime.now + # actual = DateTime.parse(header.get_request_timestamp) - expected.year.should == actual.year - expected.hour.should == actual.hour - end + # expected.year.should == actual.year + # expected.hour.should == actual.hour + # end - it "should to_hash correctly" do - # I got this example from the marketo API docs + # it "should to_hash correctly" do + # # taken from marketo API docs - access_key = 'bigcorp1_461839624B16E06BA2D663' - secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' + # access_key = 'bigcorp1_461839624B16E06BA2D663' + # secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' - header = Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key) - header.set_time(DateTime.new(2010, 4, 9, 14, 4, 55, -7/24.0)) + # header = Rapleaf::Marketo::AuthenticationHeader.new(access_key, secret_key) + # header.set_time(DateTime.new(2010, 4, 9, 14, 4, 55, -7/24.0)) - header.to_hash.should == { - 'mktowsUserId' => header.get_mktows_user_id, - 'requestSignature' => header.get_request_signature, - 'requestTimestamp' => header.get_request_timestamp, - } - end - end - end -end \ No newline at end of file + # header.to_hash.should == { + # 'mktowsUserId' => header.get_mktows_user_id, + # 'requestSignature' => header.get_request_signature, + # 'requestTimestamp' => header.get_request_timestamp, + # } + # end + # end + # end +#end \ No newline at end of file diff --git a/spec/marketo/client_spec.rb b/spec/marketo/client_spec.rb index dc64a0e..409556c 100644 --- a/spec/marketo/client_spec.rb +++ b/spec/marketo/client_spec.rb @@ -1,6 +1,6 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) -module Rapleaf +module Grabcad module Marketo describe Client do diff --git a/spec/marketo/lead_key_spec.rb b/spec/marketo/lead_key_spec.rb index 5c9f18b..021f6de 100644 --- a/spec/marketo/lead_key_spec.rb +++ b/spec/marketo/lead_key_spec.rb @@ -1,6 +1,6 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) -module Rapleaf +module Grabcad module Marketo describe LeadKeyType do it "should define the correct types" do diff --git a/spec/marketo/lead_record_spec.rb b/spec/marketo/lead_record_spec.rb index 550756b..f79991c 100644 --- a/spec/marketo/lead_record_spec.rb +++ b/spec/marketo/lead_record_spec.rb @@ -1,6 +1,6 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) -module Rapleaf +module Grabcad module Marketo TEST_EMAIL = 'some@email.com' TEST_IDNUM = 93480938 From 3e25bb81be181a9438c0244a4d5ab33b760177fe Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 22 Jul 2013 09:26:49 -0400 Subject: [PATCH 21/30] update rspec tests to cover basic use cases in new API --- lib/marketo/client.rb | 3 +- marketo.gemspec | 2 +- spec/fixtures/get_lead_response.xml | 40 +++ spec/fixtures/insert_lead_response.xml | 43 +++ spec/fixtures/sync_lead_response.xml | 43 +++ spec/marketo/client_spec.rb | 411 ++++--------------------- spec/marketo/lead_record_spec.rb | 42 ++- 7 files changed, 224 insertions(+), 360 deletions(-) create mode 100644 spec/fixtures/get_lead_response.xml create mode 100644 spec/fixtures/insert_lead_response.xml create mode 100644 spec/fixtures/sync_lead_response.xml diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index a9a2129..323238d 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -64,7 +64,8 @@ class Client def initialize(savon_client, authentication_header) @client = savon_client @header = authentication_header - if Rails && Rails.logger + + if ENV["RAILS_ENV"] && Rails && Rails.logger logger=Rails.logger else logger=Logger.new(STDOUT) diff --git a/marketo.gemspec b/marketo.gemspec index e7e6baf..b1d1227 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.7" + gem.version = "0.0.8" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Grabcad::Marketo::Client' diff --git a/spec/fixtures/get_lead_response.xml b/spec/fixtures/get_lead_response.xml new file mode 100644 index 0000000..df444c5 --- /dev/null +++ b/spec/fixtures/get_lead_response.xml @@ -0,0 +1,40 @@ + + + + + + 1 + + + 12345 + some@email.com + + + + + City + string + Utopia + + + FirstName + string + Test + + + LastName + string + Lead + + + MiddleName + string + This + + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/insert_lead_response.xml b/spec/fixtures/insert_lead_response.xml new file mode 100644 index 0000000..173da88 --- /dev/null +++ b/spec/fixtures/insert_lead_response.xml @@ -0,0 +1,43 @@ + + + + + + 12345 + + 12345 + CREATED + + + + 12345 + some@email.com + + + + + Company + string + Initech + + + FirstName + string + Test + + + LastName + string + Lead + + + Website + url + initech.com + + + + + + + \ No newline at end of file diff --git a/spec/fixtures/sync_lead_response.xml b/spec/fixtures/sync_lead_response.xml new file mode 100644 index 0000000..59ec5dc --- /dev/null +++ b/spec/fixtures/sync_lead_response.xml @@ -0,0 +1,43 @@ + + + + + + 12345 + + 12345 + UPDATED + + + + 12345 + some@email.com + + + + + City + string + Utopia + + + FirstName + string + Test + + + LastName + string + Lead + + + MiddleName + string + Updated + + + + + + + \ No newline at end of file diff --git a/spec/marketo/client_spec.rb b/spec/marketo/client_spec.rb index 409556c..f611f0d 100644 --- a/spec/marketo/client_spec.rb +++ b/spec/marketo/client_spec.rb @@ -1,363 +1,80 @@ require File.expand_path('../spec_helper', File.dirname(__FILE__)) +require 'savon/mock/spec_helper' module Grabcad module Marketo - describe Client do - EMAIL = "some@email.com" - IDNUM = 29 - FIRST = 'Joe' - LAST = 'Smith' - COMPANY = 'ABC Enterprises' - MOBILE = '415 123 456' - API_KEY = 'API123KEY' - - context 'Exception handling' do - it "should return nil if any exception is raised on get_lead request" do - savon_client = mock('savon_client').as_null_object - authentication_header = mock('authentication_header').as_null_object - client = Rapleaf::Marketo::Client.new(savon_client, authentication_header) - savon_client.should_receive(:request).and_raise Exception - client.get_lead_by_email(EMAIL).should be_nil - end - - it "should return nil if any exception is raised on sync_lead request" do - savon_client = mock('savon_client').as_null_object - authentication_header = mock('authentication_header').as_null_object - client = Rapleaf::Marketo::Client.new(savon_client, authentication_header) - savon_client.should_receive(:request).and_raise Exception - client.sync_lead(EMAIL, FIRST, LAST, COMPANY, MOBILE).should be_nil - end + include Savon::SpecHelper + # since the test does not specify a local WSDL, it will exhibit the behavior defined here https://github.com/savonrb/savon/issues/396 + + EMAIL = "some@email.com" + IDNUM = 12345 + FIRST = 'Test' + LAST = 'Lead' + COMPANY = 'Initech' + MOBILE = '415 123 456' + + ACCESS_KEY = 'ACCESS_KEY' + SECRET_KEY = 'SECRET_KEY' + ENDPOINT = "mock_endpoint" + + before(:all) { savon.mock! } + after(:all) { savon.unmock! } + + it "should return a marketo client" do + client = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) + client.should_not be_nil; end - context 'Client interaction' do - it "should have the correct body format on get_lead_by_idnum" do - savon_client = mock('savon_client') - authentication_header = mock('authentication_header') - client = Rapleaf::Marketo::Client.new(savon_client, authentication_header) - response_hash = { - :success_get_lead => { - :result => { - :count => 1, - :lead_record_list => { - :lead_record => { - :email => EMAIL, - :lead_attribute_list => { - :attribute => [ - {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, - {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, - {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, - {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} - ] - }, - :foreign_sys_type => nil, - :foreign_sys_person_id => nil, - :id => IDNUM.to_s - } - } - } - } - } - expect_request(savon_client, - authentication_header, - equals_matcher(:lead_key => { - :key_value => IDNUM, - :key_type => LeadKeyType::IDNUM - }), - 'ns1:paramsGetLead', - response_hash) - expected_lead_record = LeadRecord.new(EMAIL, IDNUM) - expected_lead_record.set_attribute('name1', 'val1') - expected_lead_record.set_attribute('name2', 'val2') - expected_lead_record.set_attribute('name3', 'val3') - expected_lead_record.set_attribute('name4', 'val4') - client.get_lead_by_idnum(IDNUM).should == expected_lead_record - end - - it "should have the correct body format on get_lead_by_email" do - savon_client = mock('savon_client') - authentication_header = mock('authentication_header') - client = Rapleaf::Marketo::Client.new(savon_client, authentication_header) - response_hash = { - :success_get_lead => { - :result => { - :count => 1, - :lead_record_list => { - :lead_record => { - :email => EMAIL, - :lead_attribute_list => { - :attribute => [ - {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, - {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, - {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, - {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} - ] - }, - :foreign_sys_type => nil, - :foreign_sys_person_id => nil, - :id => IDNUM.to_s - } - } - } - } - } - expect_request(savon_client, - authentication_header, - equals_matcher({:lead_key => { - :key_value => EMAIL, - :key_type => LeadKeyType::EMAIL}}), - 'ns1:paramsGetLead', - response_hash) - expected_lead_record = LeadRecord.new(EMAIL, IDNUM) - expected_lead_record.set_attribute('name1', 'val1') - expected_lead_record.set_attribute('name2', 'val2') - expected_lead_record.set_attribute('name3', 'val3') - expected_lead_record.set_attribute('name4', 'val4') - client.get_lead_by_email(EMAIL).should == expected_lead_record - end - - it "should have the correct body format on sync_lead_record" do - savon_client = mock('savon_client') - authentication_header = mock('authentication_header') - client = Rapleaf::Marketo::Client.new(savon_client, authentication_header) - response_hash = { - :success_sync_lead => { - :result => { - :lead_id => IDNUM, - :sync_status => { - :error => nil, - :status => 'UPDATED', - :lead_id => IDNUM - }, - :lead_record => { - :email => EMAIL, - :lead_attribute_list => { - :attribute => [ - {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, - {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, - {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, - {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} - ] - }, - :foreign_sys_type => nil, - :foreign_sys_person_id => nil, - :id => IDNUM.to_s - } - } - } - } - expect_request(savon_client, - authentication_header, - (Proc.new do |actual| - retval = true - retval = false unless actual[:return_lead] - retval = false unless actual[:lead_record][:email].equal?(EMAIL) - retval = false unless actual[:lead_record][:lead_attribute_list][:attribute].size == 5 - retval = false unless actual[:lead_record][:lead_attribute_list][:attribute].include?({:attr_value => EMAIL, :attr_name => "Email", :attr_type => "string"}) - retval = false unless actual[:lead_record][:lead_attribute_list][:attribute].include?({:attr_value => "val1", :attr_name => "name1", :attr_type => "string"}) - retval = false unless actual[:lead_record][:lead_attribute_list][:attribute].include?({:attr_value => "val2", :attr_name => "name2", :attr_type => "string"}) - retval = false unless actual[:lead_record][:lead_attribute_list][:attribute].include?({:attr_value => "val3", :attr_name => "name3", :attr_type => "string"}) - retval = false unless actual[:lead_record][:lead_attribute_list][:attribute].include?({:attr_value => "val4", :attr_name => "name4", :attr_type => "string"}) - retval.should == true - end), - 'ns1:paramsSyncLead', - response_hash) - lead_record = LeadRecord.new(EMAIL, IDNUM) - lead_record.set_attribute('name1', 'val1') - lead_record.set_attribute('name2', 'val2') - lead_record.set_attribute('name3', 'val3') - lead_record.set_attribute('name4', 'val4') - - client.sync_lead_record(lead_record).should == lead_record - end - - it "should have the correct body format on sync_lead" do - savon_client = mock('savon_client') - authentication_header = mock('authentication_header') - client = Rapleaf::Marketo::Client.new(savon_client, authentication_header) - response_hash = { - :success_sync_lead => { - :result => { - :lead_id => IDNUM, - :sync_status => { - :error => nil, - :status => 'UPDATED', - :lead_id => IDNUM - }, - :lead_record => { - :email => EMAIL, - :lead_attribute_list => { - :attribute => [ - {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, - {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, - {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, - {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} - ] - }, - :foreign_sys_type => nil, - :foreign_sys_person_id => nil, - :id => IDNUM.to_s - } - } - } - } - - expect_request(savon_client, - authentication_header, - Proc.new { |actual| - actual_attribute_list = actual[:lead_record][:lead_attribute_list][:attribute] - actual[:lead_record][:lead_attribute_list][:attribute] = nil - expected = { - :return_lead => true, - :lead_record => { - :email => "some@email.com", - :lead_attribute_list => - { - :attribute => nil}} - } - actual.should == expected - actual_attribute_list.should =~ [ - {:attr_value => FIRST, - :attr_name => "FirstName", - :attr_type => "string"}, - {:attr_value => LAST, - :attr_name => "LastName", - :attr_type => "string"}, - {:attr_value => EMAIL, - :attr_name =>"Email", - :attr_type => "string"}, - {:attr_value => COMPANY, - :attr_name => "Company", - :attr_type => "string"}, - {:attr_value => MOBILE, - :attr_name => "MobilePhone", - :attr_type => "string"} - ] - }, - 'ns1:paramsSyncLead', - response_hash) - expected_lead_record = LeadRecord.new(EMAIL, IDNUM) - expected_lead_record.set_attribute('name1', 'val1') - expected_lead_record.set_attribute('name2', 'val2') - expected_lead_record.set_attribute('name3', 'val3') - expected_lead_record.set_attribute('name4', 'val4') - client.sync_lead(EMAIL, FIRST, LAST, COMPANY, MOBILE).should == expected_lead_record - end - - context "list operations" do - LIST_KEY = 'awesome leads list' + it "should get a lead by email" do + fixture = File.read("spec/fixtures/get_lead_response.xml") + savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"EMAIL", :key_value=>"some@email.com"}} ).returns(fixture) - before(:each) do - @savon_client = mock('savon_client') - @authentication_header = mock('authentication_header') - @client = Rapleaf::Marketo::Client.new(@savon_client, @authentication_header) - end - - it "should have the correct body format on add_to_list" do - response_hash = {} # TODO - expect_request(@savon_client, - @authentication_header, - equals_matcher({ - :list_operation => ListOperationType::ADD_TO, - :list_key => LIST_KEY, - :strict => 'false', - :list_member_list => { - :lead_key => [ - { - :key_type => 'EMAIL', - :key_value => EMAIL - } - ] - } - }), - 'ns1:paramsListOperation', - response_hash) - - @client.add_to_list(LIST_KEY, EMAIL).should == response_hash - end - - it "should have the correct body format on remove_from_list" do - response_hash = {} # TODO - expect_request(@savon_client, - @authentication_header, - equals_matcher({ - :list_operation => ListOperationType::REMOVE_FROM, - :list_key => LIST_KEY, - :strict => 'false', - :list_member_list => { - :lead_key => [ - { - :key_type => 'EMAIL', - :key_value => EMAIL - } - ] - } - }), - 'ns1:paramsListOperation', - response_hash) - - @client.remove_from_list(LIST_KEY, EMAIL).should == response_hash - end - - it "should have the correct body format on is_member_of_list?" do - response_hash = {} # TODO - expect_request(@savon_client, - @authentication_header, - equals_matcher({ - :list_operation => ListOperationType::IS_MEMBER_OF, - :list_key => LIST_KEY, - :strict => 'false', - :list_member_list => { - :lead_key => [ - { - :key_type => 'EMAIL', - :key_value => EMAIL - } - ] - } - }), - 'ns1:paramsListOperation', - response_hash) - - @client.is_member_of_list?(LIST_KEY, EMAIL).should == response_hash - end - end + marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) + marketo.logger=(Logger.new(STDOUT)) + marketo.log_level = Logger::DEBUG + lead = marketo.get_lead_by_email(EMAIL) + lead.should_not be_nil + lead.email.should == EMAIL end - private - - def equals_matcher(expected) - Proc.new { |actual| - actual.should == expected - } - end + it "should get a lead by id" do + fixture = File.read("spec/fixtures/get_lead_response.xml") + savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"IDNUM", :key_value=>12345}} ).returns(fixture) - def expect_request(savon_client, authentication_header, expected_body_matcher, expected_namespace, response_hash) - header_hash = stub('header_hash') - soap_response = stub('soap_response') - request_namespace = mock('namespace') - request_header = mock('request_header') - soap_request = mock('soap_request') - authentication_header.should_receive(:set_time) - authentication_header.should_receive(:to_hash).and_return(header_hash) - request_namespace.should_receive(:[]=).with("xmlns:ns1", "http://www.marketo.com/mktows/") - request_header.should_receive(:[]=).with("ns1:AuthenticationHeader", header_hash) - soap_request.should_receive(:namespaces).and_return(request_namespace) - soap_request.should_receive(:header).and_return(request_header) - soap_request.should_receive(:body=) do |actual_body| - expected_body_matcher.call(actual_body) - end - soap_response.should_receive(:to_hash).and_return(response_hash) - savon_client.should_receive(:request).with(expected_namespace).and_yield(soap_request).and_return(soap_response) + marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) + marketo.logger=(Logger.new(STDOUT)) + marketo.log_level = Logger::DEBUG + lead = marketo.get_lead_by_lead_id(IDNUM) + lead.should_not be_nil + lead.id.should == IDNUM end - end - describe ListOperationType do - it 'should define the correct types' do - ListOperationType::ADD_TO.should == 'ADDTOLIST' - ListOperationType::IS_MEMBER_OF.should == 'ISMEMBEROFLIST' - ListOperationType::REMOVE_FROM.should == 'REMOVEFROMLIST' + it "should create a new lead" do + fixture2 = File.read("spec/fixtures/insert_lead_response.xml") + savon.expects(:sync_lead).with(message: {:return_lead=>true, + :lead_record=> + {:email=>"some@email.com", + :lead_attribute_list=> + {:attribute=> + [{:attr_name=>"Email", + :attr_type=>"string", + :attr_value=>"some@email.com"}, + {:attr_name=>"FirstName", :attr_type=>"string", :attr_value=>"Test"}, + {:attr_name=>"LastName", :attr_type=>"string", :attr_value=>"Lead"}, + {:attr_name=>"Company", + :attr_type=>"string", + :attr_value=>"Initech"}]}}} ).returns(fixture2) + marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) + marketo.logger=(Logger.new(STDOUT)) + marketo.log_level = Logger::DEBUG + lead = marketo.upsert_lead(EMAIL, FIRST, LAST, COMPANY) + lead.get_attribute("FirstName").should == FIRST + lead.email.should == EMAIL + lead.get_attribute("LastName").should == LAST + lead.get_attribute("Company").should == COMPANY + lead.id.should == IDNUM end end end -end \ No newline at end of file +end diff --git a/spec/marketo/lead_record_spec.rb b/spec/marketo/lead_record_spec.rb index f79991c..8f56854 100644 --- a/spec/marketo/lead_record_spec.rb +++ b/spec/marketo/lead_record_spec.rb @@ -4,23 +4,23 @@ module Grabcad module Marketo TEST_EMAIL = 'some@email.com' TEST_IDNUM = 93480938 - describe LeadRecord do + let (:marketo_client) {double} it "should store the idnum" do - lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) - lead_record.idnum.should == TEST_IDNUM + lead_record = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) + lead_record.id.should == TEST_IDNUM end it "should store the email" do - LeadRecord.new(TEST_EMAIL, TEST_IDNUM).email.should == TEST_EMAIL + LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM).email.should == TEST_EMAIL end it "should implement == sensibly" do - lead_record1 = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + lead_record1 = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) lead_record1.set_attribute('favourite color', 'red') lead_record1.set_attribute('age', '100') - lead_record2 = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + lead_record2 = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) lead_record2.set_attribute('favourite color', 'red') lead_record2.set_attribute('age', '100') @@ -28,20 +28,20 @@ module Marketo end it "should store when attributes are set" do - lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + lead_record = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) lead_record.set_attribute('favourite color', 'red') lead_record.get_attribute('favourite color').should == 'red' end it "should store when attributes are updated" do - lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + lead_record = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) lead_record.set_attribute('favourite color', 'red') lead_record.set_attribute('favourite color', 'green') lead_record.get_attribute('favourite color').should == 'green' end it "should yield all attributes through each_attribute_pair" do - lead_record = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + lead_record = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) lead_record.set_attribute('favourite color', 'red') lead_record.set_attribute('favourite color', 'green') lead_record.set_attribute('age', '99') @@ -72,15 +72,35 @@ module Marketo :id => TEST_IDNUM } - actual = LeadRecord.from_hash(savon_hash) + actual = LeadRecord.from_hash(marketo_client, savon_hash) - expected = LeadRecord.new(TEST_EMAIL, TEST_IDNUM) + expected = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) expected.set_attribute('Company', 'Rapleaf') expected.set_attribute('FirstName', 'James') expected.set_attribute('LastName', 'O\'Brien') actual.should == expected end + + it "sync should call client to sync via ID with marketo backend" do + savon_hash = { + :email => TEST_EMAIL, + :foreign_sys_type => nil, + :lead_attribute_list => { + :attribute => [ + { :attr_name => 'Company', :attr_type => 'string', :attr_value => 'Rapleaf'}, + { :attr_name => 'FirstName', :attr_type => 'string', :attr_value => 'James'}, + { :attr_name => 'LastName', :attr_type => 'string', :attr_value => 'O\'Brien'} + ] + }, + :foreign_sys_person_id => nil, + :id => TEST_IDNUM + } + marketo_client.should_receive(:sync_lead_record_by_id).with(anything).and_return(savon_hash) + lead_record = LeadRecord.new(marketo_client, TEST_EMAIL, TEST_IDNUM) + lead_record.sync + + end end end end \ No newline at end of file From dfbc6d6efcdc1d0e3a808df4e1886a7f41f2ecf3 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 22 Jul 2013 09:46:49 -0400 Subject: [PATCH 22/30] change default WSDL to bundled marketo API 2.1 WSDL as optimization and to prevent unit tests from needing network access --- lib/marketo/client.rb | 10 +- lib/wsdl/marketo_2_1.wsdl | 1630 +++++++++++++++++++++++++++++++++++++ marketo.gemspec | 2 +- 3 files changed, 1638 insertions(+), 4 deletions(-) create mode 100644 lib/wsdl/marketo_2_1.wsdl diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index 323238d..36bd19d 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -2,10 +2,15 @@ module Grabcad module Marketo - def self.create_client (access_key, secret_key, endpoint_uri, wsdl_uri = "http://app.marketo.com/soap/mktows/2_1?WSDL") + def self.create_client (access_key, secret_key, endpoint_uri, wsdl_uri = nil) savon_client = Savon.client do endpoint endpoint_uri - wsdl wsdl_uri + if wsdl_uri + wsdl wsdl_uri + else + #default to Marketo API version 2.1 with cached WSDL + wsdl File.expand_path('../wsdl/marketo_2_1.wsdl', File.dirname(__FILE__)) + end read_timeout 90 open_timeout 90 headers ({ "Connection" => "Keep-Alive" }) @@ -13,7 +18,6 @@ def self.create_client (access_key, secret_key, endpoint_uri, wsdl_uri = "http:/ log false pretty_print_xml true end - Client.new(savon_client, Grabcad::Marketo::AuthenticationHeader.new(access_key, secret_key)) end diff --git a/lib/wsdl/marketo_2_1.wsdl b/lib/wsdl/marketo_2_1.wsdl new file mode 100644 index 0000000..820772e --- /dev/null +++ b/lib/wsdl/marketo_2_1.wsdl @@ -0,0 +1,1630 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Delete one or more MObject. + + + + + + Get meta data for a MObject. + + + + + + Get one or more MObject. + + + + + + Create, update, or upsert MObject. + + + + + + Get a list campaigns. + + + + + + Provides information regarding the status of list import. + + + + + + Get all details about a lead. + + + + + + Get all activity log details about a lead. + + + + + + Get changes for all leads. + + + + + + Get all details about one or more leads. + + + + + + Imports the list from web file info + + + + + + Send a Marketo email. + + + + + + Perform an operation on a Marketo List, like add lead or remove lead. + + + + + + Request to add a set of leads to a campaign. + + + + + + Request to add tokens to a schedule campaign. + + + + + + Create or update a lead. + + + + + + Create or update one or more leads. + + + + + + Update, Insert, or Upsert custom object records + + + + + + Delete custom object records + + + + + + Get custom object records + + + + + + Merge Leads + + + + + + Get Tags + + + + + + Get Tags + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/marketo.gemspec b/marketo.gemspec index b1d1227..ca5cb2e 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.8" + gem.version = "0.0.9" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Grabcad::Marketo::Client' From 16dff9dbdd470f2adb6cb143ca2b511cc014ea07 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 22 Jul 2013 13:30:48 -0400 Subject: [PATCH 23/30] updated docs --- lib/marketo/client.rb | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index 36bd19d..cb25fdc 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -21,17 +21,17 @@ def self.create_client (access_key, secret_key, endpoint_uri, wsdl_uri = nil) Client.new(savon_client, Grabcad::Marketo::AuthenticationHeader.new(access_key, secret_key)) end - - # = The client for talking to marketo - WARNING: docs are old and need to be updated - # based on the SOAP wsdl file: http://app.marketo.com/soap/mktows/1_4?WSDL + # Based on the WSDL for Marketo API V2.1, cached locally in ../wsdl/marketo_2_1.wsdl. + # To select another version of the API, specify the URI to the WSDL as the optional 4th argument to create_client. + # You will need to verify that the gem still functions properly against the new version of the API. # # Usage: # - # client = Rapleaf::Marketo.new_client(, , api_subdomain = 'na-i', api_version = '1_5', document_version = '1_4') + # client = Grabcad::Marketo.create_client(, , , []) # - # == get_lead_by_email: + # == get a lead by email: # - # lead_record = client.get_lead_by_email('sombody@examnple.com') + # lead_record = client.get_lead_by_email('example@email.com') # # puts lead_record.idnum # @@ -39,32 +39,28 @@ def self.create_client (access_key, secret_key, endpoint_uri, wsdl_uri = nil) # # puts lead_record.get_attribute('LastName') # - # == sync_lead: (update) - # - # lead_record = client.sync_lead('example@rapleaf.com', 'Joe', 'Smith', 'Company 1', '415 911') - # - # == sync_lead_record: (update with custom fields) + # == insert/update a new lead: (upsert_lead) # - # lead_record = Rapleaf::Marketo::LeadRecord.new('harry@rapleaf.com') + # lead_record = client.upsert_lead('new_example@email.com', 'Test', 'Lead', 'Initech') # - # lead_record.set_attribute('FirstName', 'harry') + # == update fields: (update with custom fields) # - # lead_record.set_attribute('LastName', 'smith') + # lead_record = client.get_lead_by_email('example@email.com') # - # lead_record.set_attribute('Email', 'harry@somesite.com') + # lead_record.set_attribute('FirstName', 'NewFirst') # - # lead_record.set_attribute('Company', 'Rapleaf') + # lead_record.set_attribute('LastName', 'NewLast') # - # lead_record.set_attribute('MobilePhone', '123 456') + # lead_record.set_attribute('Email', 'updated@email.com') # - # response = client.sync_lead_record(lead_record) + # lead_record.set_attribute('Company', 'Initech2') # - # == sync_lead_record_on_id: (update with custom fields, ensuring the sync is id based) + # lead_record.set_attribute(, ) # - # similarly, you can force a sync via id instead of email by calling client.sync_lead_record_on_id(lead_record) + # lead.sync # class Client - # This constructor is used internally, create your client with *Rapleaf::Marketo.new_client(, )* + # This constructor is used internally, create your client with *Grabcad::Marketo.create_client(, , )* def initialize(savon_client, authentication_header) @client = savon_client @header = authentication_header From ef49fb83c9d2f99e31624d47efbe7d7ea597f62d Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 29 Jul 2013 14:36:41 -0400 Subject: [PATCH 24/30] fix exception with duplicate lead email addresses --- lib/marketo/lead_record.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/marketo/lead_record.rb b/lib/marketo/lead_record.rb index 8577886..678bc73 100644 --- a/lib/marketo/lead_record.rb +++ b/lib/marketo/lead_record.rb @@ -16,6 +16,10 @@ def initialize(client, email, lead_id = nil, attrs = nil) # creates and populates an instance from a savon hash returned form the marketo API def self.from_hash(client, lead_hash) + # if there's more than one lead, take the first one. + if lead_hash.kind_of? Array + lead_hash = lead_hash[0] + end lead_record = LeadRecord.new(client, lead_hash[:email], lead_hash[:id].to_i, lead_hash) end From 168c61951b8f2888e12975be3d164ba36299027b Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 29 Jul 2013 14:45:30 -0400 Subject: [PATCH 25/30] add list key type --- lib/marketo/enums.rb | 7 ++++++ lib/marketo/key.rb | 31 +++++++++++++++++++++++++++ lib/marketo/lead_key.rb | 29 ++----------------------- lib/marketo/list_key.rb | 6 ++++++ spec/marketo/key_spec.rb | 40 +++++++++++++++++++++++++++++++++++ spec/marketo/list_key_spec.rb | 40 +++++++++++++++++++++++++++++++++++ 6 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 lib/marketo/key.rb create mode 100644 lib/marketo/list_key.rb create mode 100644 spec/marketo/key_spec.rb create mode 100644 spec/marketo/list_key_spec.rb diff --git a/lib/marketo/enums.rb b/lib/marketo/enums.rb index 1a2e613..4f37763 100644 --- a/lib/marketo/enums.rb +++ b/lib/marketo/enums.rb @@ -19,5 +19,12 @@ module LeadKeyType SFDCLEADOWNERID = "SFDCLEADOWNERID" SFDCOPPTYID = "SFDCOPPTYID" end + + # Types of keys that can be used to look up a list + module ListKeyType + MKTOLISTNAME ="MKTOLISTNAME" + MKTOSALESUSERID ="MKTOSALESUSERID" + SFDCLEADOWNERID ="SFDCLEADOWNERID" + end end end \ No newline at end of file diff --git a/lib/marketo/key.rb b/lib/marketo/key.rb new file mode 100644 index 0000000..7d25862 --- /dev/null +++ b/lib/marketo/key.rb @@ -0,0 +1,31 @@ +module Grabcad + module Marketo + # Encapsulates a Marketo type-value key + class Key + # - *key_type* the type of key to use see LeadKeyType + # - *key_value* normally a string value for the given type + def initialize(key_type, key_value) + @key_type = key_type + @key_value = key_value + end + + # get the key type + def key_type + @key_type + end + + # get the key value + def key_value + @key_value + end + + # create a hash from this instance, for sending this object to marketo using savon + def to_hash + { + :key_type => @key_type, + :key_value => @key_value + } + end + end + end +end \ No newline at end of file diff --git a/lib/marketo/lead_key.rb b/lib/marketo/lead_key.rb index ec38132..d9ebeee 100644 --- a/lib/marketo/lead_key.rb +++ b/lib/marketo/lead_key.rb @@ -1,31 +1,6 @@ module Grabcad module Marketo - # Encapsulates a key used to look up or describe a specific marketo lead. - class LeadKey - # - *key_type* the type of key to use see LeadKeyType - # - *key_value* normally a string value for the given type - def initialize(key_type, key_value) - @key_type = key_type - @key_value = key_value - end - - # get the key type - def key_type - @key_type - end - - # get the key value - def key_value - @key_value - end - - # create a hash from this instance, for sending this object to marketo using savon - def to_hash - { - :key_type => @key_type, - :key_value => @key_value - } - end + class LeadKey < Key end end -end \ No newline at end of file +end diff --git a/lib/marketo/list_key.rb b/lib/marketo/list_key.rb new file mode 100644 index 0000000..35e364f --- /dev/null +++ b/lib/marketo/list_key.rb @@ -0,0 +1,6 @@ +module Grabcad + module Marketo + class ListKey < Key + end + end +end diff --git a/spec/marketo/key_spec.rb b/spec/marketo/key_spec.rb new file mode 100644 index 0000000..021f6de --- /dev/null +++ b/spec/marketo/key_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../spec_helper', File.dirname(__FILE__)) + +module Grabcad + module Marketo + describe LeadKeyType do + it "should define the correct types" do + LeadKeyType::IDNUM.should == 'IDNUM' + LeadKeyType::COOKIE.should == 'COOKIE' + LeadKeyType::EMAIL.should == 'EMAIL' + LeadKeyType::LEADOWNEREMAIL.should == 'LEADOWNEREMAIL' + LeadKeyType::SFDCACCOUNTID.should == 'SFDCACCOUNTID' + LeadKeyType::SFDCCONTACTID.should == 'SFDCCONTACTID' + LeadKeyType::SFDCLEADID.should == 'SFDCLEADID' + LeadKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID' + LeadKeyType::SFDCOPPTYID.should == 'SFDCOPPTYID' + end + end + + TEST_KEY_VALUE = 'a value' + TEST_KEY_TYPE = LeadKeyType::IDNUM + + describe LeadKey do + it "should store type and value on construction" do + + lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + lead_key.key_type.should == TEST_KEY_VALUE + lead_key.key_value.should == TEST_KEY_VALUE + end + + it "should to_hash correctly" do + lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + + lead_key.to_hash.should == { + :key_type => TEST_KEY_VALUE, + :key_value => TEST_KEY_VALUE + } + end + end + end +end diff --git a/spec/marketo/list_key_spec.rb b/spec/marketo/list_key_spec.rb new file mode 100644 index 0000000..021f6de --- /dev/null +++ b/spec/marketo/list_key_spec.rb @@ -0,0 +1,40 @@ +require File.expand_path('../spec_helper', File.dirname(__FILE__)) + +module Grabcad + module Marketo + describe LeadKeyType do + it "should define the correct types" do + LeadKeyType::IDNUM.should == 'IDNUM' + LeadKeyType::COOKIE.should == 'COOKIE' + LeadKeyType::EMAIL.should == 'EMAIL' + LeadKeyType::LEADOWNEREMAIL.should == 'LEADOWNEREMAIL' + LeadKeyType::SFDCACCOUNTID.should == 'SFDCACCOUNTID' + LeadKeyType::SFDCCONTACTID.should == 'SFDCCONTACTID' + LeadKeyType::SFDCLEADID.should == 'SFDCLEADID' + LeadKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID' + LeadKeyType::SFDCOPPTYID.should == 'SFDCOPPTYID' + end + end + + TEST_KEY_VALUE = 'a value' + TEST_KEY_TYPE = LeadKeyType::IDNUM + + describe LeadKey do + it "should store type and value on construction" do + + lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + lead_key.key_type.should == TEST_KEY_VALUE + lead_key.key_value.should == TEST_KEY_VALUE + end + + it "should to_hash correctly" do + lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + + lead_key.to_hash.should == { + :key_type => TEST_KEY_VALUE, + :key_value => TEST_KEY_VALUE + } + end + end + end +end From 89843ef94534af6f72cd84deda122cd3d4388c58 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 29 Jul 2013 14:46:35 -0400 Subject: [PATCH 26/30] add lead type --- lib/marketo.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/marketo.rb b/lib/marketo.rb index 8d607b7..64b0465 100644 --- a/lib/marketo.rb +++ b/lib/marketo.rb @@ -4,7 +4,9 @@ require File.expand_path('marketo/client', File.dirname(__FILE__)) require File.expand_path('marketo/authentication_header', File.dirname(__FILE__)) require File.expand_path('marketo/enums', File.dirname(__FILE__)) +require File.expand_path('marketo/key', File.dirname(__FILE__)) require File.expand_path('marketo/lead_key', File.dirname(__FILE__)) +require File.expand_path('marketo/list_key', File.dirname(__FILE__)) require File.expand_path('marketo/lead_record', File.dirname(__FILE__)) From ea5f375083233717259e745cb68067e05a934245 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 29 Jul 2013 16:36:12 -0400 Subject: [PATCH 27/30] add support for list operations --- lib/marketo/client.rb | 65 +++++++++++---------- lib/marketo/lead_record.rb | 15 ++++- spec/fixtures/add_lead_to_list.xml | 11 ++++ spec/fixtures/is_lead_in_list_false.xml | 19 ++++++ spec/fixtures/is_lead_in_list_true.xml | 19 ++++++ spec/fixtures/remove_lead_from_list.xml | 11 ++++ spec/marketo/client_spec.rb | 77 ++++++++++++++++++++++--- spec/marketo/key_spec.rb | 33 ++++------- spec/marketo/lead_key_spec.rb | 17 +++--- spec/marketo/list_key_spec.rb | 35 +++++------ 10 files changed, 215 insertions(+), 87 deletions(-) create mode 100644 spec/fixtures/add_lead_to_list.xml create mode 100644 spec/fixtures/is_lead_in_list_false.xml create mode 100644 spec/fixtures/is_lead_in_list_true.xml create mode 100644 spec/fixtures/remove_lead_from_list.xml diff --git a/lib/marketo/client.rb b/lib/marketo/client.rb index cb25fdc..b634655 100644 --- a/lib/marketo/client.rb +++ b/lib/marketo/client.rb @@ -177,18 +177,20 @@ def sync_lead_record_by_id(lead_record) end end - #Remove list operations until their implementation can be reviewed - # def add_to_list(list_key, email) - # list_operation(list_key, ListOperationType::ADD_TO, email) - # end + #Returns true if the operation succeeded, false otherwise + def add_to_list(list_id, lead) + list_operation(list_id_key(list_id), ListOperationType::ADD_TO, lead.id)[:success_list_operation][:result][:success] + end - # def remove_from_list(list_key, email) - # list_operation(list_key, ListOperationType::REMOVE_FROM, email) - # end + #Returns true if the operation succeeded, false otherwise + def remove_from_list(list_id, lead) + list_operation(list_id_key(list_id), ListOperationType::REMOVE_FROM, lead.id)[:success_list_operation][:result][:success] + end - # def is_member_of_list?(list_key, email) - # list_operation(list_key, ListOperationType::IS_MEMBER_OF, email) - # end + #Returns true if the operation succeeded, false otherwise + def is_member_of_list?(list_id, lead) + list_operation(list_id_key(list_id), ListOperationType::IS_MEMBER_OF, lead.id)[:success_list_operation][:result][:status_list][:lead_status][:status] + end def enable_soap_debugging @client.pretty_print_xml = true @@ -201,26 +203,31 @@ def log_exception(exp) end end + # private + def list_operation(list_key, list_operation_type, id) + begin + response = send_request(:list_operation, { + :list_operation => list_operation_type, + :list_key => list_key.to_hash, + :strict => 'false', + :list_member_list => { + :lead_key => [ + {:key_type => LeadKeyType::IDNUM, :key_value => id} + ] + } + }) + return response + rescue Exception => e + log_exception e + return nil + end + end + + def list_id_key(list_id) + ListKey.new(ListKeyType::MKTOLISTNAME, list_id) + end + private - #Remove list operations until their implementation can be reviewed - #def list_operation(list_key, list_operation_type, email) - # begin - # response = send_request(:list_operation, { - # :list_operation => list_operation_type, - # :list_key => list_key, - # :strict => 'false', - # :list_member_list => { - # :lead_key => [ - # {:key_type => 'EMAIL', :key_value => email} - # ] - # } - # }) - # return response - # rescue Exception => e - # log_exception e - # return nil - # end - # end def get_lead(lead_key) begin diff --git a/lib/marketo/lead_record.rb b/lib/marketo/lead_record.rb index 678bc73..808e702 100644 --- a/lib/marketo/lead_record.rb +++ b/lib/marketo/lead_record.rb @@ -58,6 +58,19 @@ def ==(other) @id == other.id end + # List operations + def add_to_list(list_id) + @client.add_to_list(list_id, self) + end + + def remove_from_list(list_id) + @client.remove_from_list(list_id, self) + end + + def is_member_of_list?(list_id) + @client.is_member_of_list?(list_id, self) + end + private def populate_from(lead_hash) @@ -76,7 +89,7 @@ def populate_from(lead_hash) end self rescue Exception => e - client.log_exception e + @client.log_exception e nil end end diff --git a/spec/fixtures/add_lead_to_list.xml b/spec/fixtures/add_lead_to_list.xml new file mode 100644 index 0000000..e536d7f --- /dev/null +++ b/spec/fixtures/add_lead_to_list.xml @@ -0,0 +1,11 @@ + + + + + + true + + + + + \ No newline at end of file diff --git a/spec/fixtures/is_lead_in_list_false.xml b/spec/fixtures/is_lead_in_list_false.xml new file mode 100644 index 0000000..c9bf537 --- /dev/null +++ b/spec/fixtures/is_lead_in_list_false.xml @@ -0,0 +1,19 @@ + + + + + + true + + + + IDNUM + 12345 + + false + + + + + + \ No newline at end of file diff --git a/spec/fixtures/is_lead_in_list_true.xml b/spec/fixtures/is_lead_in_list_true.xml new file mode 100644 index 0000000..9e18307 --- /dev/null +++ b/spec/fixtures/is_lead_in_list_true.xml @@ -0,0 +1,19 @@ + + + + + + true + + + + IDNUM + 12345 + + true + + + + + + \ No newline at end of file diff --git a/spec/fixtures/remove_lead_from_list.xml b/spec/fixtures/remove_lead_from_list.xml new file mode 100644 index 0000000..e536d7f --- /dev/null +++ b/spec/fixtures/remove_lead_from_list.xml @@ -0,0 +1,11 @@ + + + + + + true + + + + + \ No newline at end of file diff --git a/spec/marketo/client_spec.rb b/spec/marketo/client_spec.rb index f611f0d..7c3e62f 100644 --- a/spec/marketo/client_spec.rb +++ b/spec/marketo/client_spec.rb @@ -18,6 +18,8 @@ module Marketo SECRET_KEY = 'SECRET_KEY' ENDPOINT = "mock_endpoint" + TEST_LIST = "TestList" + before(:all) { savon.mock! } after(:all) { savon.unmock! } @@ -28,7 +30,7 @@ module Marketo it "should get a lead by email" do fixture = File.read("spec/fixtures/get_lead_response.xml") - savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"EMAIL", :key_value=>"some@email.com"}} ).returns(fixture) + savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"EMAIL", :key_value=>EMAIL}} ).returns(fixture) marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) marketo.logger=(Logger.new(STDOUT)) @@ -40,7 +42,7 @@ module Marketo it "should get a lead by id" do fixture = File.read("spec/fixtures/get_lead_response.xml") - savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"IDNUM", :key_value=>12345}} ).returns(fixture) + savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"IDNUM", :key_value=>IDNUM}} ).returns(fixture) marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) marketo.logger=(Logger.new(STDOUT)) @@ -51,7 +53,7 @@ module Marketo end it "should create a new lead" do - fixture2 = File.read("spec/fixtures/insert_lead_response.xml") + fixture = File.read("spec/fixtures/insert_lead_response.xml") savon.expects(:sync_lead).with(message: {:return_lead=>true, :lead_record=> {:email=>"some@email.com", @@ -59,12 +61,12 @@ module Marketo {:attribute=> [{:attr_name=>"Email", :attr_type=>"string", - :attr_value=>"some@email.com"}, - {:attr_name=>"FirstName", :attr_type=>"string", :attr_value=>"Test"}, - {:attr_name=>"LastName", :attr_type=>"string", :attr_value=>"Lead"}, + :attr_value=>EMAIL}, + {:attr_name=>"FirstName", :attr_type=>"string", :attr_value=>FIRST}, + {:attr_name=>"LastName", :attr_type=>"string", :attr_value=>LAST}, {:attr_name=>"Company", :attr_type=>"string", - :attr_value=>"Initech"}]}}} ).returns(fixture2) + :attr_value=>COMPANY}]}}} ).returns(fixture) marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) marketo.logger=(Logger.new(STDOUT)) marketo.log_level = Logger::DEBUG @@ -75,6 +77,67 @@ module Marketo lead.get_attribute("Company").should == COMPANY lead.id.should == IDNUM end + + describe "given a lead" do + before(:all) do + fixture = File.read("spec/fixtures/get_lead_response.xml") + savon.expects(:get_lead).with(message: {:lead_key=>{:key_type=>"IDNUM", :key_value=>IDNUM}} ).returns(fixture) + + @marketo = Grabcad::Marketo.create_client(ACCESS_KEY, SECRET_KEY, ENDPOINT) + @marketo.logger=(Logger.new(STDOUT)) + @marketo.log_level = Logger::DEBUG + @lead = @marketo.get_lead_by_lead_id(IDNUM) + end + + it "should check if a lead is a member of a list and return false" do + fixture = File.read("spec/fixtures/is_lead_in_list_false.xml") + savon.expects(:list_operation).with(message: + {:list_operation=>"ISMEMBEROFLIST", + :list_key=>{:key_type=>"MKTOLISTNAME", + :key_value=>TEST_LIST}, + :strict=>"false", + :list_member_list=>{:lead_key=>[{:key_type=>"IDNUM", :key_value=>IDNUM}]}}).returns(fixture) + + @marketo.is_member_of_list?( TEST_LIST, @lead).should be false + end + + it "should check if a lead is a member of a list and return true" do + fixture = File.read("spec/fixtures/is_lead_in_list_true.xml") + savon.expects(:list_operation).with(message: + {:list_operation=>"ISMEMBEROFLIST", + :list_key=>{:key_type=>"MKTOLISTNAME", + :key_value=>TEST_LIST}, + :strict=>"false", + :list_member_list=>{:lead_key=>[{:key_type=>"IDNUM", :key_value=>IDNUM}]}}).returns(fixture) + + @marketo.is_member_of_list?( TEST_LIST, @lead).should be true + end + + it "should add a lead to a list" do + fixture = File.read("spec/fixtures/add_lead_to_list.xml") + savon.expects(:list_operation).with(message: + {:list_operation=>"ADDTOLIST", + :list_key=>{:key_type=>"MKTOLISTNAME", + :key_value=>TEST_LIST}, + :strict=>"false", + :list_member_list=>{:lead_key=>[{:key_type=>"IDNUM", :key_value=>IDNUM}]}}).returns(fixture) + + + @marketo.add_to_list(TEST_LIST, @lead).should be true + end + + it "should remove a lead from a list" do + fixture = File.read("spec/fixtures/remove_lead_from_list.xml") + savon.expects(:list_operation).with(message: + {:list_operation=>"REMOVEFROMLIST", + :list_key=>{:key_type=>"MKTOLISTNAME", + :key_value=>TEST_LIST}, + :strict=>"false", + :list_member_list=>{:lead_key=>[{:key_type=>"IDNUM", :key_value=>IDNUM}]}}).returns(fixture) + + @marketo.remove_from_list(TEST_LIST, @lead).should be true + end + end end end end diff --git a/spec/marketo/key_spec.rb b/spec/marketo/key_spec.rb index 021f6de..2e5ec96 100644 --- a/spec/marketo/key_spec.rb +++ b/spec/marketo/key_spec.rb @@ -2,36 +2,25 @@ module Grabcad module Marketo - describe LeadKeyType do - it "should define the correct types" do - LeadKeyType::IDNUM.should == 'IDNUM' - LeadKeyType::COOKIE.should == 'COOKIE' - LeadKeyType::EMAIL.should == 'EMAIL' - LeadKeyType::LEADOWNEREMAIL.should == 'LEADOWNEREMAIL' - LeadKeyType::SFDCACCOUNTID.should == 'SFDCACCOUNTID' - LeadKeyType::SFDCCONTACTID.should == 'SFDCCONTACTID' - LeadKeyType::SFDCLEADID.should == 'SFDCLEADID' - LeadKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID' - LeadKeyType::SFDCOPPTYID.should == 'SFDCOPPTYID' - end - end - TEST_KEY_VALUE = 'a value' - TEST_KEY_TYPE = LeadKeyType::IDNUM - describe LeadKey do + describe Key do + + TEST_KEY_VALUE = 'a value' + TEST_KEY_TYPE = "type" + it "should store type and value on construction" do - lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) - lead_key.key_type.should == TEST_KEY_VALUE - lead_key.key_value.should == TEST_KEY_VALUE + key = Key.new(TEST_KEY_TYPE, TEST_KEY_VALUE) + key.key_type.should == TEST_KEY_TYPE + key.key_value.should == TEST_KEY_VALUE end it "should to_hash correctly" do - lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + key = Key.new(TEST_KEY_TYPE, TEST_KEY_VALUE) - lead_key.to_hash.should == { - :key_type => TEST_KEY_VALUE, + key.to_hash.should == { + :key_type => TEST_KEY_TYPE, :key_value => TEST_KEY_VALUE } end diff --git a/spec/marketo/lead_key_spec.rb b/spec/marketo/lead_key_spec.rb index 021f6de..ad69def 100644 --- a/spec/marketo/lead_key_spec.rb +++ b/spec/marketo/lead_key_spec.rb @@ -16,23 +16,24 @@ module Marketo end end - TEST_KEY_VALUE = 'a value' - TEST_KEY_TYPE = LeadKeyType::IDNUM describe LeadKey do + TEST_LEAD_KEY_VALUE = 'a value' + TEST_LEAD_KEY_TYPE = "test key" + it "should store type and value on construction" do - lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) - lead_key.key_type.should == TEST_KEY_VALUE - lead_key.key_value.should == TEST_KEY_VALUE + lead_key = LeadKey.new(TEST_LEAD_KEY_TYPE, TEST_KEY_VALUE) + lead_key.key_type.should == TEST_LEAD_KEY_TYPE + lead_key.key_value.should == TEST_LEAD_KEY_VALUE end it "should to_hash correctly" do - lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + lead_key = LeadKey.new(TEST_LEAD_KEY_TYPE, TEST_LEAD_KEY_VALUE) lead_key.to_hash.should == { - :key_type => TEST_KEY_VALUE, - :key_value => TEST_KEY_VALUE + :key_type => TEST_LEAD_KEY_TYPE, + :key_value => TEST_LEAD_KEY_VALUE } end end diff --git a/spec/marketo/list_key_spec.rb b/spec/marketo/list_key_spec.rb index 021f6de..d431dea 100644 --- a/spec/marketo/list_key_spec.rb +++ b/spec/marketo/list_key_spec.rb @@ -2,37 +2,32 @@ module Grabcad module Marketo - describe LeadKeyType do + describe ListKeyType do it "should define the correct types" do - LeadKeyType::IDNUM.should == 'IDNUM' - LeadKeyType::COOKIE.should == 'COOKIE' - LeadKeyType::EMAIL.should == 'EMAIL' - LeadKeyType::LEADOWNEREMAIL.should == 'LEADOWNEREMAIL' - LeadKeyType::SFDCACCOUNTID.should == 'SFDCACCOUNTID' - LeadKeyType::SFDCCONTACTID.should == 'SFDCCONTACTID' - LeadKeyType::SFDCLEADID.should == 'SFDCLEADID' - LeadKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID' - LeadKeyType::SFDCOPPTYID.should == 'SFDCOPPTYID' + ListKeyType::MKTOLISTNAME.should == 'MKTOLISTNAME' + ListKeyType::MKTOSALESUSERID.should == 'MKTOSALESUSERID' + ListKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID' + end end - TEST_KEY_VALUE = 'a value' - TEST_KEY_TYPE = LeadKeyType::IDNUM - - describe LeadKey do + describe ListKey do + TEST_LIST_KEY_VALUE = 'a value' + TEST_LIST_KEY_TYPE = "key type" + it "should store type and value on construction" do - lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) - lead_key.key_type.should == TEST_KEY_VALUE - lead_key.key_value.should == TEST_KEY_VALUE + lead_key = ListKey.new(TEST_LIST_KEY_TYPE, TEST_LIST_KEY_VALUE) + lead_key.key_type.should == TEST_LIST_KEY_TYPE + lead_key.key_value.should == TEST_LIST_KEY_VALUE end it "should to_hash correctly" do - lead_key = LeadKey.new(TEST_KEY_VALUE, TEST_KEY_VALUE) + lead_key = ListKey.new(TEST_LIST_KEY_TYPE, TEST_LIST_KEY_VALUE) lead_key.to_hash.should == { - :key_type => TEST_KEY_VALUE, - :key_value => TEST_KEY_VALUE + :key_type => TEST_LIST_KEY_TYPE, + :key_value => TEST_LIST_KEY_VALUE } end end From c2d03c0f8494f78d2cca9fc65fe2cd38f995125b Mon Sep 17 00:00:00 2001 From: Joseph George Date: Mon, 29 Jul 2013 16:37:04 -0400 Subject: [PATCH 28/30] update version to 0.0.10 --- marketo.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marketo.gemspec b/marketo.gemspec index ca5cb2e..17f2a4d 100644 --- a/marketo.gemspec +++ b/marketo.gemspec @@ -11,7 +11,7 @@ Gem::Specification.new do |gem| gem.files = Dir['lib/**/*.rb'] gem.require_path = ['lib'] gem.test_files = Dir['spec/**/*_spec.rb'] - gem.version = "0.0.9" + gem.version = "0.0.10" gem.has_rdoc = true gem.rdoc_options << '--title' << 'Marketo Client Gem, updated' << '--main' << 'Grabcad::Marketo::Client' From 1cd72e6f5e31b33d2ffc6be3add5bc21392fbf67 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Tue, 15 Mar 2016 15:07:19 -0400 Subject: [PATCH 29/30] Create PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..5a7f523 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,8 @@ +Description: +Code reviewed by: +Tested by: +PM reviewed by: +TeamCity passed: [yes] +Test plan: +Risk/complexity: [high|low] +Impact/scope: [high|low] From 501ae76e24f01d1d9dbed846eea2e862c0e60341 Mon Sep 17 00:00:00 2001 From: Joseph George Date: Tue, 15 Mar 2016 15:10:12 -0400 Subject: [PATCH 30/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5dd8c7e..ee0d00f 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,4 @@ marketo_gem =========== Fork of the original Rapleaf marketo gem to support updates to the Marketo API and fix some bugs. -This is still in experimental stages - use at your own risk until it becomes more stable! +Use at your own risk.