├── .gitignore ├── Gemfile ├── lib ├── markety │ ├── version.rb │ ├── enums.rb │ ├── lead_key.rb │ ├── multi_leads_key.rb │ ├── authentication_header.rb │ ├── lead_record.rb │ └── client.rb └── markety.rb ├── spec ├── spec_helper.rb └── markety │ ├── multi_leads_key_spec.rb │ ├── lead_key_spec.rb │ ├── authentication_header_spec.rb │ ├── lead_record_spec.rb │ └── client_spec.rb ├── bin └── markety ├── markety.gemspec └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | Gemfile.lock -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec -------------------------------------------------------------------------------- /lib/markety/version.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | 3 | VERSION = "1.3.3" 4 | 5 | end 6 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "rspec" 3 | 4 | require File.expand_path('../lib/markety', File.dirname(__FILE__)) -------------------------------------------------------------------------------- /bin/markety: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | begin 4 | require 'markety' 5 | rescue LoadError 6 | require 'rubygems' 7 | require 'markety' 8 | end -------------------------------------------------------------------------------- /lib/markety.rb: -------------------------------------------------------------------------------- 1 | require 'savon' 2 | require 'markety/authentication_header' 3 | require 'markety/client' 4 | require 'markety/enums' 5 | require 'markety/lead_key' 6 | require 'markety/multi_leads_key' 7 | require 'markety/lead_record' 8 | -------------------------------------------------------------------------------- /lib/markety/enums.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | # Types of operations you can do on a marketo list 3 | module ListOperationType 4 | ADD_TO = 'ADDTOLIST' 5 | REMOVE_FROM = 'REMOVEFROMLIST' 6 | IS_MEMBER_OF = 'ISMEMBEROFLIST' 7 | end 8 | 9 | # Types of keys that can be used to look up a lead 10 | module LeadKeyType 11 | IDNUM = "IDNUM" 12 | COOKIE = "COOKIE" 13 | EMAIL = "EMAIL" 14 | LEADOWNEREMAIL = "LEADOWNEREMAIL" 15 | SFDCACCOUNTID = "SFDCACCOUNTID" 16 | SFDCCONTACTID = "SFDCCONTACTID" 17 | SFDCLEADID = "SFDCLEADID" 18 | SFDCLEADOWNERID = "SFDCLEADOWNERID" 19 | SFDCOPPTYID = "SFDCOPPTYID" 20 | end 21 | end -------------------------------------------------------------------------------- /lib/markety/lead_key.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | # Encapsulates a key used to look up or describe a specific marketo lead. 3 | class LeadKey 4 | # - *key_type* the type of key to use see LeadKeyType 5 | # - *key_value* normally a string value for the given type 6 | def initialize(key_type, key_value) 7 | @key_type = key_type 8 | @key_value = key_value 9 | end 10 | 11 | # get the key type 12 | def key_type 13 | @key_type 14 | end 15 | 16 | # get the key value 17 | def key_value 18 | @key_value 19 | end 20 | 21 | # create a hash from this instance, for sending this object to marketo using savon 22 | def to_hash 23 | { 24 | "keyType" => @key_type, 25 | "keyValue" => @key_value 26 | } 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /markety.gemspec: -------------------------------------------------------------------------------- 1 | $:.push File.expand_path("../lib", __FILE__) 2 | require 'markety/version' 3 | 4 | Gem::Specification.new do |s| 5 | s.name = "markety" 6 | s.version = Markety::VERSION 7 | s.summary = "Marketo SOAP API integration" 8 | s.description = "A client to allow easy integration with Marketo's SOAP API" 9 | s.authors = "David Santoso" 10 | s.email = "david.e.santoso@gmail.com" 11 | s.homepage = "https://github.com/davidsantoso/markety" 12 | s.files = Dir['lib/**/*.rb'] 13 | s.require_path = 'lib' 14 | s.executables << 'markety' 15 | s.required_ruby_version = '>= 1.9.3' 16 | s.add_dependency 'savon', '= 2.3.1' 17 | s.add_dependency 'wasabi', '= 3.2.1' 18 | s.add_development_dependency 'rspec', '>= 2.3.0' 19 | end 20 | -------------------------------------------------------------------------------- /lib/markety/multi_leads_key.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | # Encapsulates a key used to look up or describe multiple marketo leads. 3 | class MultiLeadsKey 4 | # - *key_type* the type of key to use see LeadKeyType 5 | # - *key_values* normally an array of string values for the given type 6 | def initialize(key_type, key_values) 7 | @key_type = key_type 8 | @key_values = key_values 9 | end 10 | 11 | # get the key type 12 | def key_type 13 | @key_type 14 | end 15 | 16 | # get the key values 17 | def key_values 18 | @key_values 19 | end 20 | 21 | # create a hash from this instance, for sending this object to marketo using savon 22 | def to_hash 23 | { 24 | "keyType" => @key_type, 25 | "keyValues" => @key_values 26 | } 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /spec/markety/multi_leads_key_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../spec_helper', File.dirname(__FILE__)) 2 | 3 | module Markety 4 | describe LeadKey do 5 | it "should store type and values on construction" do 6 | KEY_VALUES = ['a value', 'another value'] 7 | KEY_TYPE = LeadKeyType::IDNUM 8 | lead_key = MultiLeadsKey.new(KEY_TYPE, KEY_VALUES) 9 | lead_key.key_type.should == KEY_TYPE 10 | lead_key.key_values.should == KEY_VALUES 11 | end 12 | 13 | it "should to_hash correctly" do 14 | KEY_VALUES = ['a value', 'another value'] 15 | KEY_TYPE = LeadKeyType::IDNUM 16 | lead_key = MultiLeadsKey.new(KEY_TYPE, KEY_VALUES) 17 | 18 | lead_key.to_hash.should == { 19 | "keyType" => KEY_TYPE, 20 | "keyValues" => KEY_VALUES 21 | } 22 | end 23 | end 24 | end -------------------------------------------------------------------------------- /lib/markety/authentication_header.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | # This class exists only to encapsulate the authentication header part of a soap request to marketo 3 | class AuthenticationHeader 4 | DIGEST = OpenSSL::Digest::Digest.new('sha1') 5 | 6 | def initialize(access_key, secret_key, time = DateTime.now) 7 | @access_key = access_key 8 | @secret_key = secret_key 9 | @time = time 10 | end 11 | 12 | public 13 | # time should be a DateTime instance 14 | def set_time(time) 15 | @time = time 16 | end 17 | 18 | def get_mktows_user_id 19 | @access_key 20 | end 21 | 22 | def get_request_signature 23 | calculate_signature 24 | end 25 | 26 | def get_request_timestamp 27 | @time.to_s 28 | end 29 | 30 | def to_hash 31 | { "ns1:AuthenticationHeader" => 32 | { 33 | "mktowsUserId" => get_mktows_user_id, 34 | "requestSignature" => get_request_signature, 35 | "requestTimestamp" => get_request_timestamp 36 | } 37 | } 38 | end 39 | 40 | private 41 | def calculate_signature 42 | request_timestamp = get_request_timestamp 43 | string_to_encrypt = request_timestamp + @access_key 44 | OpenSSL::HMAC.hexdigest(DIGEST, @secret_key, string_to_encrypt) 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Markety 2 | Easily integrate with the Marketo SOAP API to find and update leads. 3 | 4 | This is a fork off of the [Rapleaf marketo_gem] (https://github.com/Rapleaf/marketo_gem) but has been updated to work with Savon v2.3.1. It makes connecting to your Marketo database to find and update leads a snap. If you're having problems connecting to Marketo, please submit an issue as there have been lots of changes with [Savon] (https://github.com/savonrb/savon) lately. 5 | 6 | ## Install 7 | Add this your Gemfile: 8 | 9 | ```ruby 10 | gem 'markety' 11 | ``` 12 | 13 | and run bundle install. 14 | 15 | ## Examples 16 | 17 | ```ruby 18 | # Instantiate a new Markety client using your Marketo SOAP endpoint, User ID, and Encryption Key 19 | client = Markety.new_client(USER_ID, ENCRYPTION_KEY, END_POINT) 20 | 21 | # Get a lead from the Marketo database 22 | lead = client.get_lead_by_email("joe@example.com") 23 | 24 | # Update a lead record 25 | lead.set_attribute("Email", "joe-schmoe@example.com") 26 | 27 | # Update a lead record with an attribute that isn't a string 28 | lead.set_attribute("Activated", true, "Boolean") 29 | 30 | # Sync the lead with Marketo 31 | response = client.sync_lead_record(lead) 32 | 33 | # Check your lead database in Marketo to see the changes! 34 | ``` 35 | 36 | ## Help and Docs 37 | 38 | * I still have to write some official documentation. -------------------------------------------------------------------------------- /spec/markety/lead_key_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../spec_helper', File.dirname(__FILE__)) 2 | 3 | module Markety 4 | describe LeadKeyType do 5 | it "should define the correct types" do 6 | LeadKeyType::IDNUM.should == 'IDNUM' 7 | LeadKeyType::COOKIE.should == 'COOKIE' 8 | LeadKeyType::EMAIL.should == 'EMAIL' 9 | LeadKeyType::LEADOWNEREMAIL.should == 'LEADOWNEREMAIL' 10 | LeadKeyType::SFDCACCOUNTID.should == 'SFDCACCOUNTID' 11 | LeadKeyType::SFDCCONTACTID.should == 'SFDCCONTACTID' 12 | LeadKeyType::SFDCLEADID.should == 'SFDCLEADID' 13 | LeadKeyType::SFDCLEADOWNERID.should == 'SFDCLEADOWNERID' 14 | LeadKeyType::SFDCOPPTYID.should == 'SFDCOPPTYID' 15 | end 16 | end 17 | 18 | describe LeadKey do 19 | it "should store type and value on construction" do 20 | KEY_VALUE = 'a value' 21 | KEY_TYPE = LeadKeyType::IDNUM 22 | lead_key = LeadKey.new(KEY_TYPE, KEY_VALUE) 23 | lead_key.key_type.should == KEY_TYPE 24 | lead_key.key_value.should == KEY_VALUE 25 | end 26 | 27 | it "should to_hash correctly" do 28 | KEY_VALUE = 'a value' 29 | KEY_TYPE = LeadKeyType::IDNUM 30 | lead_key = LeadKey.new(KEY_TYPE, KEY_VALUE) 31 | 32 | lead_key.to_hash.should == { 33 | "keyType" => KEY_TYPE, 34 | "keyValue" => KEY_VALUE 35 | } 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/markety/lead_record.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | # Represents a record of the data known about a lead within marketo 3 | class LeadRecord 4 | attr_reader :types 5 | 6 | def initialize(email, idnum = nil) 7 | @idnum = idnum 8 | @attributes = {} 9 | @types = {} 10 | set_attribute('Email', email) 11 | end 12 | 13 | # hydrates an instance from a savon hash returned form the marketo API 14 | def self.from_hash(savon_hash) 15 | lead_record = LeadRecord.new(savon_hash[:email], savon_hash[:id].to_i) 16 | attribute_list = savon_hash[:lead_attribute_list][:attribute] 17 | # savon converts a result set of one into a single hash 18 | unless attribute_list.is_a? Array 19 | attribute_list = [attribute_list] 20 | end 21 | attribute_list.each do |attribute| 22 | lead_record.set_attribute(attribute[:attr_name], attribute[:attr_value], attribute[:attr_type]) 23 | end 24 | lead_record 25 | end 26 | 27 | def self.from_hash_list(leads_list) 28 | results = [] 29 | 30 | # savon converts a result set of one into a single hash 31 | unless leads_list.is_a? Array 32 | leads_list = [leads_list] 33 | end 34 | 35 | for savon_hash in leads_list 36 | lead_record = LeadRecord.new(savon_hash[:email], savon_hash[:id].to_i) 37 | savon_hash[:lead_attribute_list][:attribute].each do |attribute| 38 | lead_record.set_attribute(attribute[:attr_name], attribute[:attr_value], attribute[:attr_type]) 39 | end 40 | results << lead_record 41 | end 42 | 43 | results 44 | end 45 | 46 | # get the record idnum 47 | def idnum 48 | @idnum 49 | end 50 | 51 | # get the record email 52 | def email 53 | get_attribute('Email') 54 | end 55 | 56 | def attributes 57 | @attributes 58 | end 59 | 60 | # update the value of the named attribute 61 | def set_attribute(name, value, type = "string") 62 | @attributes[name] = value 63 | @types[name] = type 64 | end 65 | 66 | # get the value for the named attribute 67 | def get_attribute(name) 68 | @attributes[name] 69 | end 70 | 71 | def get_attribute_type(name) 72 | @types[name] 73 | end 74 | 75 | # will yield pairs of |attribute_name, attribute_value| 76 | def each_attribute_pair(&block) 77 | @attributes.each_pair do |name, value| 78 | block.call(name, value) 79 | end 80 | end 81 | 82 | def ==(other) 83 | @attributes == other.attributes && 84 | @idnum == other.idnum 85 | end 86 | end 87 | end -------------------------------------------------------------------------------- /spec/markety/authentication_header_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../spec_helper', File.dirname(__FILE__)) 2 | 3 | module Markety 4 | ACCESS_KEY = 'ACCESS_KEY' 5 | SECRET_KEY = 'SECRET_KEY' 6 | 7 | describe AuthenticationHeader do 8 | it "should set mktowsUserId to access key" do 9 | header = Markety::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) 10 | header.get_mktows_user_id.should == ACCESS_KEY 11 | end 12 | 13 | it "should set requestSignature" do 14 | header = Markety::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) 15 | header.get_request_signature.should_not be_nil 16 | header.get_request_signature.should_not == '' 17 | end 18 | 19 | it "should set requestTimestamp in correct format" do 20 | header = Markety::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) 21 | time = DateTime.new(1998, 1, 17, 20, 15, 1) 22 | header.set_time(time) 23 | header.get_request_timestamp().should == '1998-01-17T20:15:01+00:00' 24 | end 25 | 26 | it "should calculate encrypted signature" do 27 | # I got this example of the marketo API docs 28 | 29 | access_key = 'bigcorp1_461839624B16E06BA2D663' 30 | secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' 31 | 32 | header = Markety::AuthenticationHeader.new(access_key, secret_key) 33 | header.set_time(DateTime.new(2010, 4, 9, 14, 4, 54, -7/24.0)) 34 | 35 | header.get_request_timestamp.should == '2010-04-09T14:04:54-07:00' 36 | header.get_request_signature.should == 'ffbff4d4bef354807481e66dc7540f7890523a87' 37 | end 38 | 39 | it "should cope if no date is given" do 40 | header = Markety::AuthenticationHeader.new(ACCESS_KEY, SECRET_KEY) 41 | expected = DateTime.now 42 | actual = DateTime.parse(header.get_request_timestamp) 43 | 44 | expected.year.should == actual.year 45 | expected.hour.should == actual.hour 46 | end 47 | 48 | it "should to_hash correctly" do 49 | # I got this example from the marketo API docs 50 | 51 | access_key = 'bigcorp1_461839624B16E06BA2D663' 52 | secret_key = '899756834129871744AAEE88DDCC77CDEEDEC1AAAD66' 53 | 54 | header = Markety::AuthenticationHeader.new(access_key, secret_key) 55 | header.set_time(DateTime.new(2010, 4, 9, 14, 4, 55, -7/24.0)) 56 | 57 | header.to_hash.should == {"ns1:AuthenticationHeader" => 58 | { 59 | 'mktowsUserId' => header.get_mktows_user_id, 60 | 'requestSignature' => header.get_request_signature, 61 | 'requestTimestamp' => header.get_request_timestamp, 62 | } 63 | } 64 | end 65 | end 66 | end -------------------------------------------------------------------------------- /spec/markety/lead_record_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../spec_helper', File.dirname(__FILE__)) 2 | 3 | module Markety 4 | EMAIL = 'some@email.com' 5 | IDNUM = 93480938 6 | 7 | describe LeadRecord do 8 | it "should store the idnum" do 9 | lead_record = LeadRecord.new(EMAIL, IDNUM) 10 | lead_record.idnum.should == IDNUM 11 | end 12 | 13 | it "should store the email" do 14 | LeadRecord.new(EMAIL, IDNUM).email.should == EMAIL 15 | end 16 | 17 | it "should implement == sensibly" do 18 | lead_record1 = LeadRecord.new(EMAIL, IDNUM) 19 | lead_record1.set_attribute('favourite color', 'red') 20 | lead_record1.set_attribute('age', '100') 21 | 22 | lead_record2 = LeadRecord.new(EMAIL, IDNUM) 23 | lead_record2.set_attribute('favourite color', 'red') 24 | lead_record2.set_attribute('age', '100') 25 | 26 | lead_record1.should == lead_record2 27 | end 28 | 29 | it "should store when attributes are set" do 30 | lead_record = LeadRecord.new(EMAIL, IDNUM) 31 | lead_record.set_attribute('favourite color', 'red') 32 | lead_record.get_attribute('favourite color').should == 'red' 33 | end 34 | 35 | it "should store when attributes are updated" do 36 | lead_record = LeadRecord.new(EMAIL, IDNUM) 37 | lead_record.set_attribute('favourite color', 'red') 38 | lead_record.set_attribute('favourite color', 'green') 39 | lead_record.get_attribute('favourite color').should == 'green' 40 | end 41 | 42 | it "should yield all attributes through each_attribute_pair" do 43 | lead_record = LeadRecord.new(EMAIL, IDNUM) 44 | lead_record.set_attribute('favourite color', 'red') 45 | lead_record.set_attribute('favourite color', 'green') 46 | lead_record.set_attribute('age', '99') 47 | 48 | pairs = [] 49 | lead_record.each_attribute_pair do |attribute_name, attribute_value| 50 | pairs << [attribute_name, attribute_value] 51 | end 52 | 53 | pairs.size.should == 3 54 | pairs.should include(['favourite color', 'green']) 55 | pairs.should include(['age', '99']) 56 | pairs.should include(['Email', EMAIL]) 57 | end 58 | 59 | it "should be instantiable from a savon hash" do 60 | savon_hash = { 61 | :email => EMAIL, 62 | :foreign_sys_type => nil, 63 | :lead_attribute_list => { 64 | :attribute => [ 65 | { :attr_name => 'Company', :attr_type => 'string', :attr_value => 'Yaya'}, 66 | { :attr_name => 'FirstName', :attr_type => 'string', :attr_value => 'James'}, 67 | { :attr_name => 'LastName', :attr_type => 'string', :attr_value => 'O\'Brien'} 68 | ] 69 | }, 70 | :foreign_sys_person_id => nil, 71 | :id => IDNUM 72 | } 73 | 74 | actual = LeadRecord.from_hash(savon_hash) 75 | 76 | expected = LeadRecord.new(EMAIL, IDNUM) 77 | expected.set_attribute('Company', 'Yaya') 78 | expected.set_attribute('FirstName', 'James') 79 | expected.set_attribute('LastName', 'O\'Brien') 80 | 81 | actual.should == expected 82 | end 83 | end 84 | end -------------------------------------------------------------------------------- /lib/markety/client.rb: -------------------------------------------------------------------------------- 1 | module Markety 2 | def self.new_client(access_key, secret_key, end_point, api_version = '2_2', log = false) 3 | client = Savon.client do 4 | endpoint end_point 5 | wsdl "http://app.marketo.com/soap/mktows/#{api_version}?WSDL" 6 | env_namespace "SOAP-ENV" 7 | namespaces({"xmlns:ns1" => "http://www.marketo.com/mktows/"}) 8 | pretty_print_xml true 9 | log log 10 | end 11 | 12 | Client.new(client, Markety::AuthenticationHeader.new(access_key, secret_key)) 13 | end 14 | 15 | class Client 16 | def initialize(savon_client, authentication_header) 17 | @client = savon_client 18 | @header = authentication_header 19 | end 20 | 21 | public 22 | 23 | # multiple lead functionality 24 | def get_multiple_leads_by_idnum(idnums) 25 | if !idnums.kind_of?(Array) 26 | idnums = [] 27 | end 28 | 29 | get_multiple_leads(MultiLeadsKey.new(LeadKeyType::IDNUM, idnums)) 30 | end 31 | 32 | def get_multiple_leads_by_email(emails) 33 | if !emails.kind_of?(Array) 34 | emails = [] 35 | end 36 | 37 | get_multiple_leads(MultiLeadsKey.new(LeadKeyType::EMAIL, emails)) 38 | end 39 | 40 | # lead functionality 41 | def get_lead_by_idnum(idnum) 42 | get_lead(LeadKey.new(LeadKeyType::IDNUM, idnum)) 43 | end 44 | 45 | def get_lead_by_email(email) 46 | get_lead(LeadKey.new(LeadKeyType::EMAIL, email)) 47 | end 48 | 49 | def sync_lead(email, first, last, company, mobile) 50 | lead_record = LeadRecord.new(email) 51 | lead_record.set_attribute('FirstName', first) 52 | lead_record.set_attribute('LastName', last) 53 | lead_record.set_attribute('Email', email) 54 | lead_record.set_attribute('Company', company) 55 | lead_record.set_attribute('MobilePhone', mobile) 56 | sync_lead_record(lead_record) 57 | end 58 | 59 | def sync_lead_record(lead_record) 60 | attributes = [] 61 | lead_record.each_attribute_pair do |name, value| 62 | attributes << {:attr_name => name, :attr_value => value, :attr_type => lead_record.get_attribute_type(name) } 63 | end 64 | 65 | response = send_request(:sync_lead, { 66 | :dedup_enabled => true, 67 | :return_lead => true, 68 | :lead_record => { 69 | :email => lead_record.email, 70 | :lead_attribute_list => { 71 | :attribute => attributes 72 | } 73 | } 74 | }) 75 | 76 | return LeadRecord.from_hash(response[:success_sync_lead][:result][:lead_record]) 77 | end 78 | 79 | def sync_multiple_lead_records(lead_records, attributes_to_sync = nil) 80 | lead_record_list = [] 81 | 82 | for lead_record in lead_records 83 | attributes = [] 84 | 85 | # sync them all 86 | if attributes_to_sync == nil 87 | lead_record.each_attribute_pair do |name, value| 88 | attributes << {:attr_name => name, :attr_value => value, :attr_type => lead_record.get_attribute_type(name) } 89 | end 90 | # sync this subset 91 | else 92 | # we need email for deduping 93 | if (!attributes_to_sync.include?('Email')) 94 | attributes_to_sync << 'Email' 95 | end 96 | 97 | for attribute in attributes_to_sync 98 | attributes << {:attr_name => attribute, :attr_value => lead_record.get_attribute(attribute), :attr_type => lead_record.get_attribute_type(attribute) } 99 | end 100 | end 101 | 102 | lead_record_list << { 103 | :email => lead_record.email, 104 | :lead_attribute_list => { 105 | :attribute => attributes 106 | } 107 | } 108 | end 109 | 110 | response = send_request(:sync_multiple_leads, { 111 | :dedup_enabled => true, 112 | :lead_record_list => {:lead_record => lead_record_list} 113 | }) 114 | return response[:success_sync_multiple_leads][:result][:sync_status_list] 115 | end 116 | 117 | def sync_lead_record_on_id(lead_record) 118 | idnum = lead_record.idnum 119 | raise 'lead record id not set' if idnum.nil? 120 | 121 | attributes = [] 122 | lead_record.each_attribute_pair do |name, value| 123 | attributes << {:attr_name => name, :attr_value => value} 124 | end 125 | 126 | attributes << {:attr_name => 'Id', :attr_type => 'string', :attr_value => idnum.to_s} 127 | 128 | response = send_request(:sync_lead, { 129 | :return_lead => true, 130 | :lead_record => 131 | { 132 | :lead_attribute_list => { :attribute => attributes}, 133 | :id => idnum 134 | } 135 | }) 136 | return LeadRecord.from_hash(response[:success_sync_lead][:result][:lead_record]) 137 | end 138 | 139 | # MObject functionality 140 | def list_m_objects() 141 | response = send_request(:list_m_objects, { 142 | :params_list_mobjects => [] 143 | }) 144 | return response[:success_list_m_objects][:result] 145 | end 146 | 147 | # list functionality 148 | def add_to_list(list_key, idnum) 149 | list_operation(list_key, ListOperationType::ADD_TO, idnum) 150 | end 151 | 152 | def remove_from_list(list_key, idnum) 153 | list_operation(list_key, ListOperationType::REMOVE_FROM, idnum) 154 | end 155 | 156 | def is_member_of_list?(list_key, idnum) 157 | list_operation(list_key, ListOperationType::IS_MEMBER_OF, idnum) 158 | end 159 | 160 | private 161 | 162 | def list_operation(list_key, list_operation_type, idnum) 163 | response = send_request(:list_operation, { 164 | :list_operation => list_operation_type, 165 | :list_key => { 166 | :key_type => 'MKTOLISTNAME', 167 | :key_value => list_key 168 | }, 169 | :strict => 'false', 170 | :list_member_list => { 171 | :lead_key => [ 172 | {:key_type => 'IDNUM', :key_value => idnum} 173 | ] 174 | } 175 | }) 176 | if list_operation_type == ListOperationType::IS_MEMBER_OF 177 | return response[:success_list_operation][:result][:status_list][:lead_status][:status] 178 | else 179 | return response[:success_list_operation][:result][:success] 180 | end 181 | end 182 | 183 | def get_lead(lead_key) 184 | response = send_request(:get_lead, {"leadKey" => lead_key.to_hash}) 185 | return LeadRecord.from_hash(response[:success_get_lead][:result][:lead_record_list][:lead_record]) 186 | end 187 | 188 | def get_multiple_leads(lead_key) 189 | message = { 190 | "leadSelector" => { 191 | "keyType" => lead_key.key_type, 192 | "keyValues" => { 193 | "stringItem" => lead_key.key_values 194 | } 195 | }, 196 | :attributes! => {"leadSelector" => { "xsi:type" => "ns1:LeadKeySelector" }} 197 | } 198 | response = send_request(:get_multiple_leads, message) 199 | return LeadRecord.from_hash_list(response[:success_get_multiple_leads][:result][:lead_record_list][:lead_record]) 200 | end 201 | 202 | def send_request(namespace, message) 203 | @header.set_time(DateTime.now) 204 | response = request(namespace, message, @header.to_hash) 205 | response.to_hash 206 | end 207 | 208 | def request(namespace, message, header) 209 | @client.call(namespace, :message => message, :soap_header => header) 210 | end 211 | end 212 | end 213 | -------------------------------------------------------------------------------- /spec/markety/client_spec.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../spec_helper', File.dirname(__FILE__)) 2 | 3 | module Markety 4 | 5 | describe Client do 6 | EMAIL = "some@email.com" 7 | IDNUM = 29 8 | FIRST = 'Joe' 9 | LAST = 'Smith' 10 | COMPANY = 'A Company' 11 | MOBILE = '415 123 456' 12 | API_KEY = 'API123KEY' 13 | 14 | context 'Client interaction' do 15 | it "should have the correct body format on get_lead_by_idnum" do 16 | savon_client = double('savon_client') 17 | authentication_header = double('authentication_header') 18 | client = Markety::Client.new(savon_client, authentication_header) 19 | response_hash = { 20 | :success_get_lead => { 21 | :result => { 22 | :count => 1, 23 | :lead_record_list => { 24 | :lead_record => { 25 | :email => EMAIL, 26 | :lead_attribute_list => { 27 | :attribute => [ 28 | {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, 29 | {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, 30 | {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, 31 | {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} 32 | ] 33 | }, 34 | :foreign_sys_type => nil, 35 | :foreign_sys_person_id => nil, 36 | :id => IDNUM.to_s 37 | } 38 | } 39 | } 40 | } 41 | } 42 | expect_request(savon_client, 43 | authentication_header, 44 | response_hash) 45 | expected_lead_record = LeadRecord.new(EMAIL, IDNUM) 46 | expected_lead_record.set_attribute('name1', 'val1') 47 | expected_lead_record.set_attribute('name2', 'val2') 48 | expected_lead_record.set_attribute('name3', 'val3') 49 | expected_lead_record.set_attribute('name4', 'val4') 50 | client.get_lead_by_idnum(IDNUM).should == expected_lead_record 51 | end 52 | 53 | it "should have the correct body format on get_lead_by_email" do 54 | savon_client = double('savon_client') 55 | authentication_header = double('authentication_header') 56 | client = Markety::Client.new(savon_client, authentication_header) 57 | response_hash = { 58 | :success_get_lead => { 59 | :result => { 60 | :count => 1, 61 | :lead_record_list => { 62 | :lead_record => { 63 | :email => EMAIL, 64 | :lead_attribute_list => { 65 | :attribute => [ 66 | {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, 67 | {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, 68 | {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, 69 | {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} 70 | ] 71 | }, 72 | :foreign_sys_type => nil, 73 | :foreign_sys_person_id => nil, 74 | :id => IDNUM.to_s 75 | } 76 | } 77 | } 78 | } 79 | } 80 | expect_request(savon_client, 81 | authentication_header, 82 | response_hash) 83 | expected_lead_record = LeadRecord.new(EMAIL, IDNUM) 84 | expected_lead_record.set_attribute('name1', 'val1') 85 | expected_lead_record.set_attribute('name2', 'val2') 86 | expected_lead_record.set_attribute('name3', 'val3') 87 | expected_lead_record.set_attribute('name4', 'val4') 88 | client.get_lead_by_email(EMAIL).should == expected_lead_record 89 | end 90 | 91 | it "should have the correct body format on sync_lead_record" do 92 | savon_client = double('savon_client') 93 | authentication_header = double('authentication_header') 94 | client = Markety::Client.new(savon_client, authentication_header) 95 | response_hash = { 96 | :success_sync_lead => { 97 | :result => { 98 | :lead_id => IDNUM, 99 | :sync_status => { 100 | :error => nil, 101 | :status => 'UPDATED', 102 | :lead_id => IDNUM 103 | }, 104 | :lead_record => { 105 | :email => EMAIL, 106 | :lead_attribute_list => { 107 | :attribute => [ 108 | {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, 109 | {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, 110 | {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, 111 | {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} 112 | ] 113 | }, 114 | :foreign_sys_type => nil, 115 | :foreign_sys_person_id => nil, 116 | :id => IDNUM.to_s 117 | } 118 | } 119 | } 120 | } 121 | expect_request(savon_client, 122 | authentication_header, 123 | response_hash) 124 | lead_record = LeadRecord.new(EMAIL, IDNUM) 125 | lead_record.set_attribute('name1', 'val1') 126 | lead_record.set_attribute('name2', 'val2') 127 | lead_record.set_attribute('name3', 'val3') 128 | lead_record.set_attribute('name4', 'val4') 129 | 130 | client.sync_lead_record(lead_record).should == lead_record 131 | end 132 | 133 | it "should have the correct body format on sync_multiple_lead_records" do 134 | savon_client = double('savon_client') 135 | authentication_header = double('authentication_header') 136 | client = Markety::Client.new(savon_client, authentication_header) 137 | response_hash = { 138 | :success_sync_multiple_leads => { 139 | :result => { 140 | :sync_status_list => { 141 | :sync_status => [ 142 | { 143 | :error => nil, 144 | :status => 'UPDATED', 145 | :lead_id => IDNUM 146 | }, 147 | { 148 | :error => nil, 149 | :status => 'UPDATED', 150 | :lead_id => IDNUM + 1 151 | } 152 | ] 153 | } 154 | } 155 | } 156 | } 157 | expect_request(savon_client, 158 | authentication_header, 159 | response_hash) 160 | lead_record = LeadRecord.new(EMAIL, IDNUM) 161 | lead_record2 = LeadRecord.new("foo." + EMAIL, IDNUM + 1) 162 | 163 | response = client.sync_multiple_lead_records([lead_record, lead_record2]) 164 | response[:sync_status].size.should == 2 165 | response[:sync_status][0][:status].should == "UPDATED" 166 | response[:sync_status][1][:status].should == "UPDATED" 167 | 168 | end 169 | 170 | it "should have the correct body format on sync_lead" do 171 | savon_client = double('savon_client') 172 | authentication_header = double('authentication_header') 173 | client = Markety::Client.new(savon_client, authentication_header) 174 | response_hash = { 175 | :success_sync_lead => { 176 | :result => { 177 | :lead_id => IDNUM, 178 | :sync_status => { 179 | :error => nil, 180 | :status => 'UPDATED', 181 | :lead_id => IDNUM 182 | }, 183 | :lead_record => { 184 | :email => EMAIL, 185 | :lead_attribute_list => { 186 | :attribute => [ 187 | {:attr_name => 'name1', :attr_type => 'string', :attr_value => 'val1'}, 188 | {:attr_name => 'name2', :attr_type => 'string', :attr_value => 'val2'}, 189 | {:attr_name => 'name3', :attr_type => 'string', :attr_value => 'val3'}, 190 | {:attr_name => 'name4', :attr_type => 'string', :attr_value => 'val4'} 191 | ] 192 | }, 193 | :foreign_sys_type => nil, 194 | :foreign_sys_person_id => nil, 195 | :id => IDNUM.to_s 196 | } 197 | } 198 | } 199 | } 200 | 201 | expect_request(savon_client, 202 | authentication_header, 203 | response_hash) 204 | expected_lead_record = LeadRecord.new(EMAIL, IDNUM) 205 | expected_lead_record.set_attribute('name1', 'val1') 206 | expected_lead_record.set_attribute('name2', 'val2') 207 | expected_lead_record.set_attribute('name3', 'val3') 208 | expected_lead_record.set_attribute('name4', 'val4') 209 | client.sync_lead(EMAIL, FIRST, LAST, COMPANY, MOBILE).should == expected_lead_record 210 | end 211 | 212 | context "list operations" do 213 | LIST_KEY = 'awesome leads list' 214 | 215 | before(:each) do 216 | @savon_client = double('savon_client') 217 | @authentication_header = double('authentication_header') 218 | @client = Markety::Client.new(@savon_client, @authentication_header) 219 | end 220 | 221 | it "should have the correct body format on add_to_list" do 222 | response_hash = { 223 | :success_list_operation => { 224 | :result => { 225 | :success => true, 226 | :status_list => nil 227 | } 228 | } 229 | } 230 | expect_request(@savon_client, 231 | @authentication_header, 232 | response_hash) 233 | 234 | @client.add_to_list(LIST_KEY, IDNUM).should == true 235 | end 236 | 237 | it "should have the correct body format on remove_from_list" do 238 | response_hash = { 239 | :success_list_operation => { 240 | :result => { 241 | :success => true, 242 | :status_list => nil 243 | } 244 | } 245 | } 246 | expect_request(@savon_client, 247 | @authentication_header, 248 | response_hash) 249 | 250 | @client.remove_from_list(LIST_KEY, IDNUM).should == true 251 | end 252 | 253 | it "should have the correct body format on is_member_of_list?" do 254 | response_hash = { 255 | :success_list_operation => { 256 | :result => { 257 | :success => true, 258 | :status_list => { 259 | :lead_status => { 260 | :lead_key => { 261 | :key_type => IDNUM, 262 | :key_value => 1 263 | }, 264 | :status => true 265 | } 266 | } 267 | } 268 | } 269 | } 270 | expect_request(@savon_client, 271 | @authentication_header, 272 | response_hash) 273 | 274 | @client.is_member_of_list?(LIST_KEY, IDNUM).should == true 275 | end 276 | end 277 | end 278 | 279 | private 280 | 281 | def equals_matcher(expected) 282 | Proc.new { |actual| 283 | actual.should == expected 284 | } 285 | end 286 | 287 | def expect_request(savon_client, authentication_header, response_hash) 288 | header_hash = double('header_hash') 289 | soap_response = double('soap_response') 290 | 291 | authentication_header.should_receive(:set_time) 292 | authentication_header.should_receive(:to_hash).and_return(header_hash) 293 | 294 | soap_response.should_receive(:to_hash).and_return(response_hash) 295 | savon_client.should_receive(:call).and_return(soap_response) 296 | end 297 | end 298 | 299 | describe ListOperationType do 300 | it 'should define the correct types' do 301 | ListOperationType::ADD_TO.should == 'ADDTOLIST' 302 | ListOperationType::IS_MEMBER_OF.should == 'ISMEMBEROFLIST' 303 | ListOperationType::REMOVE_FROM.should == 'REMOVEFROMLIST' 304 | end 305 | end 306 | end --------------------------------------------------------------------------------