etapi 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source(:rubygems)
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,34 @@
1
+ ExactTarget API Wrapper
2
+ =======================
3
+
4
+ ETAPI is an ExactTarget API wrapper for XML calls.
5
+
6
+ Documentation
7
+ ------------
8
+ [Wiki](https://github.com/Phiction/etapi/wiki)
9
+
10
+ TODO
11
+ ====
12
+
13
+ Subscriber Management
14
+ ---
15
+ `subscriber_add` : add s4 `:account_id`, add `:status`
16
+ `subscriber_edit` : add s4 `:account_id`, add `:status`
17
+ `subscriber_retrieve` : add s4 `:account_id`, add `:status`
18
+ `subscriber_retrieve_from_list`
19
+ `subscriber_retrieve_from_list` : add s4 `:account_id`, add `:status`
20
+ `subscriber_retrieve_lists`
21
+ `subscriber_retrieve_lists` : add s4 `:account_id`, add `:status`
22
+ `subscriber_unsubscribe_master` : multiple email addresses
23
+
24
+ List Management
25
+ ---
26
+ `list_retrieve_subscribers` : add s4 `:account_id`, add `:status`
27
+
28
+ Email Management
29
+ ---
30
+ `email_retrieve_body` : have access to sub accounts?
31
+
32
+ XML
33
+ ===
34
+ Any other XML calls available will be added by request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/etapi.gemspec ADDED
@@ -0,0 +1,22 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $:.unshift lib unless $:.include? lib
3
+
4
+ require "etapi/version"
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "etapi"
8
+ s.version = ETAPI::Version
9
+ s.authors = ["Chris McGrath"]
10
+ s.email = ["mcgrath.chris@gmail.com"]
11
+ s.homepage = %q{http://github.com/phiction/etapi}
12
+ s.summary = %q{Ruby Wrapper for Exact Target's APIs}
13
+ s.description = %q{This is an attempt to make integrating easier with Exact Target's XML APIs.}
14
+
15
+ s.rubyforge_project = "etapi"
16
+
17
+ s.add_dependency("builder", ">= 2.1.2")
18
+ s.add_dependency("nokogiri", ">= 1.4.1")
19
+
20
+ s.files = `git ls-files`.split("\n")
21
+ s.require_path = "lib"
22
+ end
@@ -0,0 +1,70 @@
1
+ module ETAPI
2
+
3
+ class Session
4
+
5
+ def build_call(type, method, *args)
6
+
7
+ options = args.extract_options!
8
+ ignore_parse = options[:ignore_parse] || false
9
+
10
+ data = ""
11
+ xml = Builder::XmlMarkup.new(:target => data, :indent => 2)
12
+ xml.instruct!
13
+ xml.exacttarget do
14
+ xml.authorization do
15
+ xml.username @username
16
+ xml.password @password
17
+ end
18
+ xml.system do
19
+ xml.system_name type
20
+ xml.action method
21
+ for parameter in @parameters
22
+ if parameter[0] == "values"
23
+ xml.values do
24
+ parameter[1].each do |key, value|
25
+ eval("xml.#{key.gsub(/\s/, '__')} '#{value}'")
26
+ end
27
+ end
28
+ else
29
+ if parameter[1].is_a?(Hash)
30
+ xml.tag!(parameter[0]) do
31
+ parameter[1].each do |key, value|
32
+ eval("xml.#{key.gsub(/\s/, '__')} '#{value}'")
33
+ end
34
+ end
35
+ else
36
+ eval("xml.#{parameter[0]} '#{parameter[1]}'")
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ data_encoded = "qf=xml&xml=" + url_encode(data)
44
+ response = @api_url.post(@api_uri.path, data_encoded, @headers.merge('Content-length' => data_encoded.length.to_s))
45
+ check_response(response)
46
+ ignore_parse ? response.read_body : Nokogiri::XML::Document.parse(response.read_body)
47
+
48
+ end
49
+
50
+ private
51
+
52
+ def print_xml(obj)
53
+ require "rexml/document"
54
+ doc = REXML::Document.new(obj.http.raw_body)
55
+ out = ""
56
+ doc.write(out, 1)
57
+ out
58
+ end
59
+
60
+ def print_full_xml(obj)
61
+ require "rexml/document"
62
+ doc = REXML::Document.new(obj)
63
+ out = ""
64
+ doc.write(out, 1)
65
+ out
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -0,0 +1,33 @@
1
+ module ETAPI
2
+
3
+ class Session
4
+
5
+ def email_retrieve_body(*args)
6
+
7
+ # options
8
+ options = args.extract_options!
9
+ @email_id = options[:email_id]
10
+
11
+ # check for required options
12
+ required_options = ["email_id"]
13
+ return false unless check_required(required_options)
14
+
15
+ # merge parameters and values
16
+ type = "email"
17
+ method = "retrieve"
18
+ @parameters = {
19
+ "search_type" => "emailid",
20
+ "sub_action" => "htmlemail",
21
+ "search_value" => @email_id,
22
+ "search_value2" => "",
23
+ "search_value3" => "",
24
+ }
25
+
26
+ response = build_call(type, method)
27
+ response.xpath("//htmlbody").text rescue false
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,28 @@
1
+ module ETAPI
2
+
3
+ class Session
4
+
5
+ def list_retrieve_subscribers(*args)
6
+
7
+ # options
8
+ options = args.extract_options!
9
+ @list_id = options[:list_id]
10
+
11
+ # check for required options
12
+ required_options = ["list_id"]
13
+ return false unless check_required(required_options)
14
+
15
+ # merge parameters and values
16
+ @parameters = {
17
+ "search_type" => "listid",
18
+ "search_value" => @list_id
19
+ }
20
+
21
+ response = build_call("list", "retrieve_sub", {:parse_response => false})
22
+ Hash.from_xml(response)['exacttarget']['system']['list']['subscribers'].first[1] rescue false
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,178 @@
1
+ module ETAPI
2
+
3
+ class Session
4
+
5
+ def subscriber_add(*args)
6
+
7
+ # options
8
+ options = args.extract_options!
9
+ @list_id = options[:list_id] ||= nil
10
+ @email = options[:email]
11
+ @values = options[:values] ||= {}
12
+ @account_id = options[:account_id]
13
+
14
+ # check for required options
15
+ required_options = ["email", "list_id"]
16
+ return false unless check_required(required_options)
17
+
18
+ # update options
19
+ @values.merge!({"Email Address" => @email, "status" => "active"})
20
+
21
+ # merge parameters and values
22
+ @parameters = {
23
+ "search_type" => "listid",
24
+ "search_value" => @list_id,
25
+ "search_value2" => "",
26
+ "values" => @values
27
+ }
28
+
29
+ response = build_call("subscriber", "add")
30
+ response.xpath("//subscriber_description").text.to_i rescue false
31
+
32
+ end
33
+
34
+ def subscriber_edit(*args)
35
+
36
+ # options
37
+ options = args.extract_options!
38
+ @subscriber_id = options[:subscriber_id]
39
+ @values = options[:values] ||= {}
40
+ @account_id = options[:account_id] ||= ""
41
+
42
+ # check for required options
43
+ required_options = ["subscriber_id"]
44
+ return false unless check_required(required_options)
45
+
46
+ # merge parameters and values
47
+ @parameters = {
48
+ "search_type" => "subid",
49
+ "search_value" => @subscriber_id,
50
+ "search_value2" => "",
51
+ "values" => @values
52
+ }
53
+
54
+ response = build_call("subscriber", "edit")
55
+ response.xpath("//subscriber_description").text == @subscriber_id.to_s rescue false
56
+
57
+ end
58
+
59
+ def subscriber_edit_from_list(*args)
60
+
61
+ # options
62
+ options = args.extract_options!
63
+ @list_id = options[:list_id]
64
+ @email = options[:email]
65
+ @values = options[:values] ||= {}
66
+ @account_id = options[:account_id] ||= ""
67
+
68
+ # check for required options
69
+ required_options = ["list_id", "email"]
70
+ return false unless check_required(required_options)
71
+
72
+ # merge parameters and values
73
+ @parameters = {
74
+ "search_type" => "listid",
75
+ "search_value" => @list_id,
76
+ "search_value2" => @email,
77
+ "values" => @values
78
+ }
79
+
80
+ response = build_call("subscriber", "edit")
81
+ response.xpath("//subscriber_description").text.blank? && response.xpath("//subscriber_info").text.blank? ? false : true rescue false
82
+
83
+ end
84
+
85
+ def subscriber_delete(*args)
86
+
87
+ # options
88
+ options = args.extract_options!
89
+ @subscriber_id = options[:subscriber_id]
90
+
91
+ # check for required options
92
+ required_options = ["subscriber_id"]
93
+ return false unless check_required(required_options)
94
+
95
+ # merge parameters and values
96
+ @parameters = {
97
+ "search_type" => "subid",
98
+ "search_value" => @subscriber_id,
99
+ "search_value2" => ""
100
+ }
101
+
102
+ response = build_call("subscriber", "delete")
103
+ response.xpath("//subscriber_info").text.blank? ? false : true rescue false
104
+
105
+ end
106
+
107
+ def subscriber_remove_from_list(*args)
108
+
109
+ # options
110
+ options = args.extract_options!
111
+ @list_id = options[:list_id]
112
+ @email = options[:email]
113
+
114
+ # check for required options
115
+ required_options = ["list_id", "email"]
116
+ return false unless check_required(required_options)
117
+
118
+ # merge parameters and values
119
+ @parameters = {
120
+ "search_type" => "listid",
121
+ "search_value" => @list_id,
122
+ "search_value2" => @email
123
+ }
124
+
125
+ response = build_call("subscriber", "delete")
126
+ response.xpath("//subscriber_info").text.blank? ? false : true rescue false
127
+
128
+ end
129
+
130
+ def subscriber_unsubscribe_master(*args)
131
+
132
+ # options
133
+ options = args.extract_options!
134
+ @email = options[:email]
135
+
136
+ # check for required options
137
+ required_options = ["email"]
138
+ return false unless check_required(required_options)
139
+
140
+ # merge parameters and values
141
+ @parameters = {
142
+ "search_type" => "emailaddress",
143
+ "search_value" => {
144
+ "emailaddress" => @email
145
+ }
146
+ }
147
+
148
+ response = build_call("subscriber", "masterunsub")
149
+ response.xpath("//emailaddress").text == @email ? true : false rescue false
150
+
151
+ end
152
+
153
+ def subscriber_retrieve(*args)
154
+
155
+ # options
156
+ options = args.extract_options!
157
+ @subscriber_id = options[:subscriber_id]
158
+ @account_id = options[:account_id] ||= ""
159
+
160
+ # check for required options
161
+ required_options = ["subscriber_id"]
162
+ return false unless check_required(required_options)
163
+
164
+ # merge parameters and values
165
+ @parameters = {
166
+ "search_type" => "subid",
167
+ "search_value" => @subscriber_id,
168
+ "search_value2" => ""
169
+ }
170
+
171
+ response = build_call("subscriber", "retrieve", {:ignore_parse => true})
172
+ Hash.from_xml(response)['exacttarget']['system']['subscriber'].first rescue false
173
+
174
+ end
175
+
176
+ end
177
+
178
+ end
@@ -0,0 +1,32 @@
1
+ module ETAPI
2
+
3
+ class Session
4
+
5
+ def tracking_retrieve(*args)
6
+
7
+ # options
8
+ options = args.extract_options!
9
+ @job_id = options[:job_id]
10
+
11
+ # check for required options
12
+ required_options = ["job_id"]
13
+ return false unless check_required(required_options)
14
+
15
+ # merge parameters and values
16
+ type = "tracking"
17
+ method = "retrieve"
18
+ @parameters = {
19
+ "search_type" => "jobID",
20
+ "sub_action" => "summary",
21
+ "search_value" => @job_id,
22
+ "search_value2" => ""
23
+ }
24
+
25
+ response = build_call(type, method, {:parse_response => false})
26
+ Hash.from_xml(response)['exacttarget']['system']['tracking']['emailSummary'] rescue false
27
+
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,49 @@
1
+ require("erb")
2
+ require("uri")
3
+ require("logger")
4
+ require("builder")
5
+ require("nokogiri")
6
+ require("net/https")
7
+ require("etapi/error")
8
+ require("etapi/exact_target")
9
+ require("etapi/call_builder")
10
+
11
+ include(ERB::Util)
12
+
13
+ module ETAPI
14
+ module Configuration
15
+
16
+ attr_accessor(
17
+ :username,
18
+ :password,
19
+ :use_s4,
20
+ :raise_errors,
21
+ :log
22
+ )
23
+
24
+ attr_writer(:log)
25
+ def log?
26
+ @log != false
27
+ end
28
+
29
+ def log(message)
30
+ logger.send(log_level, "\n#{message}\n") if log?
31
+ end
32
+
33
+ attr_writer(:logger)
34
+ def logger
35
+ @logger ||= ::Logger.new(STDOUT)
36
+ end
37
+
38
+ attr_writer(:log_level)
39
+ def log_level
40
+ @log_level ||= :debug
41
+ end
42
+
43
+ attr_writer(:raise_errors)
44
+ def raise_errors?
45
+ @raise_errors != false
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,46 @@
1
+ module ETAPI
2
+
3
+ class Session
4
+
5
+ def check_response(response)
6
+
7
+ raise Error.new(-1, 'Network error') if response.class != Net::HTTPOK
8
+
9
+ response = Nokogiri::XML::Document.parse(response.body)
10
+
11
+ error_code = response.xpath("//error")
12
+ error_msg = response.xpath("//error_description")
13
+
14
+ if !error_code.blank? && !error_msg.blank?
15
+ if ETAPI.raise_errors?
16
+ raise(RuntimeError, "\n\n Code: #{error_code.text.to_i}\n Message: #{error_msg.text}\n\n")
17
+ else
18
+ ETAPI.log(" Code: #{error_code.text.to_i}\n Message: #{error_msg.text}") if ETAPI.log?
19
+ return false
20
+ end
21
+ end
22
+
23
+ end
24
+
25
+ def check_required(required_options)
26
+ missing_options = []
27
+
28
+ for option in required_options
29
+ missing_options << ":#{option}" if eval("@#{option}.blank?")
30
+ end
31
+
32
+ if !missing_options.blank?
33
+ if ETAPI.log?
34
+ ETAPI.log(" Code: ETAPI\n Message: missing #{missing_options.join(', ')}")
35
+ elsif ETAPI.raise_errors?
36
+ raise(ArgumentError, "\n\n Code: ETAPI\n Message: missing #{missing_options.join(', ')}\n\n")
37
+ end
38
+ return false
39
+ end
40
+
41
+ return true
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,52 @@
1
+ require("etapi/calls/list")
2
+ require("etapi/calls/email")
3
+ require("etapi/calls/tracking")
4
+ require("etapi/calls/subscriber")
5
+
6
+ module ETAPI
7
+
8
+ class Session
9
+
10
+ # set default options
11
+ DEFAULTS = {
12
+ :api_method => :xml,
13
+ :use_s4 => false,
14
+ :api_uri_xml => "https://api.dc1.exacttarget.com/integrate.aspx",
15
+ :api_uri_xml_s4 => "https://api.s4.exacttarget.com/integrate.aspx",
16
+ :use_ssl => true,
17
+ }
18
+
19
+ # allowed options
20
+ ALLOWED_USE_S4_OPTIONS = [true, false]
21
+
22
+ # initialize a new exact target instance
23
+ def initialize(*args)
24
+
25
+ # merge options with configuration
26
+ options = args.extract_options!
27
+ @username = options[:username] ||= ETAPI.username
28
+ @password = options[:password] ||= ETAPI.password
29
+ @use_s4 = options[:use_s4] ||= ETAPI.use_s4 ||= DEFAULTS[:use_s4]
30
+ @headers = {"Content-Type" => "application/x-www-form-urlencoded", "Connection" => "close"}
31
+
32
+ # check for required options
33
+ raise ArgumentError, "* missing :username *" if @username.blank?
34
+ raise ArgumentError, "* missing :password *" if @password.blank?
35
+ raise ArgumentError, "* invalid :use_s4 | options => [true, false] *" unless ALLOWED_USE_S4_OPTIONS.include?(@use_s4)
36
+
37
+ @api_uri = @use_s4 ? DEFAULTS[:api_uri_xml_s4] : DEFAULTS[:api_uri_xml]
38
+
39
+ @api_wsdl = @api_uri if @use_s4
40
+
41
+ # parse uri
42
+ @api_uri = URI.parse(@api_uri)
43
+ @api_url = Net::HTTP.new(@api_uri.host, @api_uri.port)
44
+
45
+ # check for SSL (disabled)
46
+ @api_url.use_ssl = options[:use_ssl] ||= DEFAULTS[:use_ssl]
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,3 @@
1
+ module ETAPI
2
+ Version = "0.1.1"
3
+ end
data/lib/etapi.rb ADDED
@@ -0,0 +1,10 @@
1
+ require("etapi/configuration")
2
+
3
+ module ETAPI
4
+ extend Configuration
5
+
6
+ def self.configure
7
+ yield self if block_given?
8
+ end
9
+
10
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: etapi
3
+ version: !ruby/object:Gem::Version
4
+ hash: 25
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 1
10
+ version: 0.1.1
11
+ platform: ruby
12
+ authors:
13
+ - Chris McGrath
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-03 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: builder
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 15
29
+ segments:
30
+ - 2
31
+ - 1
32
+ - 2
33
+ version: 2.1.2
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: nokogiri
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 5
45
+ segments:
46
+ - 1
47
+ - 4
48
+ - 1
49
+ version: 1.4.1
50
+ type: :runtime
51
+ version_requirements: *id002
52
+ description: This is an attempt to make integrating easier with Exact Target's XML APIs.
53
+ email:
54
+ - mcgrath.chris@gmail.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files: []
60
+
61
+ files:
62
+ - .gitignore
63
+ - Gemfile
64
+ - README.md
65
+ - Rakefile
66
+ - etapi.gemspec
67
+ - lib/etapi.rb
68
+ - lib/etapi/call_builder.rb
69
+ - lib/etapi/calls/email.rb
70
+ - lib/etapi/calls/list.rb
71
+ - lib/etapi/calls/subscriber.rb
72
+ - lib/etapi/calls/tracking.rb
73
+ - lib/etapi/configuration.rb
74
+ - lib/etapi/error.rb
75
+ - lib/etapi/exact_target.rb
76
+ - lib/etapi/version.rb
77
+ homepage: http://github.com/phiction/etapi
78
+ licenses: []
79
+
80
+ post_install_message:
81
+ rdoc_options: []
82
+
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 3
100
+ segments:
101
+ - 0
102
+ version: "0"
103
+ requirements: []
104
+
105
+ rubyforge_project: etapi
106
+ rubygems_version: 1.8.10
107
+ signing_key:
108
+ specification_version: 3
109
+ summary: Ruby Wrapper for Exact Target's APIs
110
+ test_files: []
111
+