fuelsdk 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +24 -0
- data/Gemfile +3 -0
- data/Guardfile +8 -0
- data/README.md +117 -0
- data/Rakefile +1 -0
- data/fuelsdk.gemspec +30 -0
- data/lib/fuelsdk.rb +572 -0
- data/lib/fuelsdk/client.rb +142 -0
- data/lib/fuelsdk/http_request.rb +79 -0
- data/lib/fuelsdk/objects.rb +143 -0
- data/lib/fuelsdk/rest.rb +84 -0
- data/lib/fuelsdk/soap.rb +177 -0
- data/lib/fuelsdk/targeting.rb +22 -0
- data/lib/fuelsdk/version.rb +3 -0
- data/lib/new.rb +1204 -0
- data/samples/sample-AddSubscriberToList.rb +52 -0
- data/samples/sample-CreateDataExtensions.rb +51 -0
- data/samples/sample-bounceevent.rb +68 -0
- data/samples/sample-campaign.rb +212 -0
- data/samples/sample-clickevent.rb +68 -0
- data/samples/sample-contentarea.rb +114 -0
- data/samples/sample-dataextension.rb +192 -0
- data/samples/sample-email.rb +114 -0
- data/samples/sample-folder.rb +143 -0
- data/samples/sample-list.rb +105 -0
- data/samples/sample-list.subscriber.rb +97 -0
- data/samples/sample-openevent.rb +68 -0
- data/samples/sample-sentevent.rb +69 -0
- data/samples/sample-subscriber.rb +136 -0
- data/samples/sample-triggeredsend.rb +122 -0
- data/samples/sample-unsubevent.rb +68 -0
- data/samples/sample_helper.rb +8 -0
- data/spec/fuelsdk/client_spec.rb +21 -0
- data/spec/fuelsdk_spec.rb +197 -0
- data/spec/helper_funcs_spec.rb +11 -0
- data/spec/http_request_spec.rb +36 -0
- data/spec/rest_spec.rb +48 -0
- data/spec/soap_spec.rb +44 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/targeting_spec.rb +39 -0
- metadata +239 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
module FuelSDK
|
2
|
+
class ET_Response
|
3
|
+
# not doing accessor so user, can't update these values from response.
|
4
|
+
# You will see in the code some of these
|
5
|
+
# items are being updated via back doors and such.
|
6
|
+
attr_reader :code, :message, :results, :request_id, :body, :raw
|
7
|
+
|
8
|
+
# some defaults
|
9
|
+
def success
|
10
|
+
@success ||= false
|
11
|
+
end
|
12
|
+
alias :success? :success
|
13
|
+
alias :status :success # backward compatibility
|
14
|
+
|
15
|
+
def more
|
16
|
+
@more ||= false
|
17
|
+
end
|
18
|
+
alias :more? :more
|
19
|
+
|
20
|
+
def initialize raw, client
|
21
|
+
@client = client # keep connection with client in case we request more
|
22
|
+
@results = []
|
23
|
+
@raw = raw
|
24
|
+
@body = raw.body
|
25
|
+
unpack raw
|
26
|
+
rescue => ex # all else fails return raw
|
27
|
+
puts ex.message
|
28
|
+
raw
|
29
|
+
end
|
30
|
+
|
31
|
+
def continue
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def unpack raw
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class ET_Client
|
42
|
+
attr_accessor :debug, :access_token, :auth_token, :internal_token, :refresh_token,
|
43
|
+
:id, :secret, :signature
|
44
|
+
|
45
|
+
include FuelSDK::Soap
|
46
|
+
include FuelSDK::Rest
|
47
|
+
|
48
|
+
def jwt= encoded_jwt
|
49
|
+
raise 'Require app signature to decode JWT' unless self.signature
|
50
|
+
decoded_jwt = JWT.decode(encoded_jwt, self.signature, true)
|
51
|
+
|
52
|
+
self.auth_token = decoded_jwt['request']['user']['oauthToken']
|
53
|
+
self.internal_token = decoded_jwt['request']['user']['internalOauthToken']
|
54
|
+
self.refresh_token = decoded_jwt['request']['user']['refreshToken']
|
55
|
+
#@authTokenExpiration = Time.new + decoded_jwt['request']['user']['expiresIn']
|
56
|
+
end
|
57
|
+
|
58
|
+
def initialize(params={}, debug=false)
|
59
|
+
self.debug = debug
|
60
|
+
client_config = params['client']
|
61
|
+
if client_config
|
62
|
+
self.id = client_config["id"]
|
63
|
+
self.secret = client_config["secret"]
|
64
|
+
self.signature = client_config["signature"]
|
65
|
+
end
|
66
|
+
|
67
|
+
self.jwt = params['jwt'] if params['jwt']
|
68
|
+
self.refresh_token = params['refresh_token'] if params['refresh_token']
|
69
|
+
|
70
|
+
self.wsdl = params["defaultwsdl"] if params["defaultwsdl"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def refresh force=false
|
74
|
+
raise 'Require Client Id and Client Secret to refresh tokens' unless (id && secret)
|
75
|
+
|
76
|
+
if (self.access_token.nil? || force)
|
77
|
+
payload = Hash.new.tap do |h|
|
78
|
+
h['clientId']= id
|
79
|
+
h['clientSecret'] = secret
|
80
|
+
h['refreshToken'] = refresh_token if refresh_token
|
81
|
+
h['accessType'] = 'offline'
|
82
|
+
end
|
83
|
+
|
84
|
+
options = Hash.new.tap do |h|
|
85
|
+
h['data'] = payload
|
86
|
+
h['content_type'] = 'application/json'
|
87
|
+
h['params'] = {'legacy' => 1}
|
88
|
+
end
|
89
|
+
|
90
|
+
response = post("https://auth.exacttargetapis.com/v1/requestToken", options)
|
91
|
+
raise "Unable to refresh token: #{response['message']}" unless response.has_key?('accessToken')
|
92
|
+
|
93
|
+
self.access_token = response['accessToken']
|
94
|
+
self.internal_token = response['legacyToken']
|
95
|
+
#@authTokenExpiration = Time.new + tokenResponse['expiresIn']
|
96
|
+
self.refresh_token = response['refreshToken'] if response.has_key?("refreshToken")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def refresh!
|
101
|
+
refresh true
|
102
|
+
end
|
103
|
+
|
104
|
+
#def AddSubscriberToList(emailAddress, listIDs, subscriberKey = nil)
|
105
|
+
# newSub = FuelSDK::ET_Subscriber.new
|
106
|
+
# newSub.authStub = self
|
107
|
+
# lists = []
|
108
|
+
|
109
|
+
# listIDs.each{ |p|
|
110
|
+
# lists.push({"ID"=> p})
|
111
|
+
# }
|
112
|
+
|
113
|
+
# newSub.props = {"EmailAddress" => emailAddress, "Lists" => lists}
|
114
|
+
# if !subscriberKey.nil? then
|
115
|
+
# newSub.props['SubscriberKey'] = subscriberKey;
|
116
|
+
# end
|
117
|
+
|
118
|
+
# # Try to add the subscriber
|
119
|
+
# postResponse = newSub.post
|
120
|
+
|
121
|
+
# if postResponse.status == false then
|
122
|
+
# # If the subscriber already exists in the account then we need to do an update.
|
123
|
+
# # Update Subscriber On List
|
124
|
+
# if postResponse.results[0][:error_code] == "12014" then
|
125
|
+
# patchResponse = newSub.patch
|
126
|
+
# return patchResponse
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
# return postResponse
|
130
|
+
#end
|
131
|
+
|
132
|
+
#def CreateDataExtensions(dataExtensionDefinitions)
|
133
|
+
# newDEs = FuelSDK::ET_DataExtension.new
|
134
|
+
# newDEs.authStub = self
|
135
|
+
|
136
|
+
# newDEs.props = dataExtensionDefinitions
|
137
|
+
# postResponse = newDEs.post
|
138
|
+
|
139
|
+
# return postResponse
|
140
|
+
#end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'net/https'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module FuelSDK
|
6
|
+
|
7
|
+
class HTTPResponse < FuelSDK::ET_Response
|
8
|
+
|
9
|
+
def initialize raw, client, request
|
10
|
+
super raw, client
|
11
|
+
@request = request
|
12
|
+
end
|
13
|
+
|
14
|
+
def continue
|
15
|
+
rsp = nil
|
16
|
+
if more?
|
17
|
+
rsp = unpack @client.rest_get(@request['url'], @request['options'])
|
18
|
+
else
|
19
|
+
puts 'No more data'
|
20
|
+
end
|
21
|
+
|
22
|
+
rsp
|
23
|
+
end
|
24
|
+
|
25
|
+
def [] key
|
26
|
+
@results[key]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
def unpack raw
|
31
|
+
@code = raw.code.to_i
|
32
|
+
@message = raw.message
|
33
|
+
@body = JSON.parse(raw.body) rescue {}
|
34
|
+
@results = @body
|
35
|
+
@more = ((@results['count'] || @results['totalCount']) > @results['page'] * @results['pageSize']) rescue false
|
36
|
+
@success = @message == 'OK'
|
37
|
+
end
|
38
|
+
|
39
|
+
# by default try everything against results
|
40
|
+
def method_missing method, *args, &block
|
41
|
+
@results.send(method, *args, &block)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module HTTPRequest
|
46
|
+
|
47
|
+
request_methods = ['get', 'post', 'patch', 'delete']
|
48
|
+
request_methods.each do |method|
|
49
|
+
class_eval <<-EOT, __FILE__, __LINE__ + 1
|
50
|
+
def #{method}(url, options={}) # def post(url, options)
|
51
|
+
request Net::HTTP::#{method.capitalize}, url, options # request Net::HTTP::Post, url, options
|
52
|
+
end # end
|
53
|
+
EOT
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def generate_uri(url, params=nil)
|
59
|
+
uri = URI.parse(url)
|
60
|
+
uri.query = URI.encode_www_form(params) if params
|
61
|
+
uri
|
62
|
+
end
|
63
|
+
|
64
|
+
def request(method, url, options={})
|
65
|
+
uri = generate_uri url, options['params']
|
66
|
+
|
67
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
68
|
+
http.use_ssl = true
|
69
|
+
|
70
|
+
data = options['data']
|
71
|
+
_request = method.new uri.request_uri
|
72
|
+
_request.body = data.to_json if data
|
73
|
+
_request.content_type = options['content_type'] if options['content_type']
|
74
|
+
response = http.request(_request)
|
75
|
+
|
76
|
+
HTTPResponse.new(response, self, :url => url, :options => options)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module FuelSDK
|
2
|
+
module ET_SoapGet
|
3
|
+
def get
|
4
|
+
client.soap_get id, properties, filter
|
5
|
+
end
|
6
|
+
|
7
|
+
def info
|
8
|
+
client.soap_describe id
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module ET_SoapCUD #create, update, delete
|
13
|
+
def post
|
14
|
+
client.soap_post id, properties
|
15
|
+
end
|
16
|
+
|
17
|
+
def patch
|
18
|
+
client.soap_patch id, properties
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete
|
22
|
+
client.soap_delete id, properties
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module ET_RestGet
|
27
|
+
def get
|
28
|
+
client.rest_get id, properties
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ET_RestCUD
|
33
|
+
def post
|
34
|
+
client.rest_post id, properties
|
35
|
+
end
|
36
|
+
|
37
|
+
def patch
|
38
|
+
client.rest_patch id, properties
|
39
|
+
end
|
40
|
+
|
41
|
+
def delete
|
42
|
+
client.rest_delete id, properties
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class ET_Base
|
47
|
+
attr_accessor :filter, :properties, :client
|
48
|
+
attr_reader :id
|
49
|
+
|
50
|
+
alias props= properties= # backward compatibility
|
51
|
+
alias authStub= client= # backward compatibility
|
52
|
+
|
53
|
+
def id
|
54
|
+
self.class.name.split('::').pop.split('_').pop
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class ET_BounceEvent < ET_Base
|
59
|
+
include ET_SoapGet
|
60
|
+
end
|
61
|
+
|
62
|
+
class ET_ClickEvent < ET_Base
|
63
|
+
include ET_SoapGet
|
64
|
+
end
|
65
|
+
|
66
|
+
class ET_ContentArea < ET_Base
|
67
|
+
include ET_SoapGet
|
68
|
+
include ET_SoapCUD
|
69
|
+
end
|
70
|
+
|
71
|
+
class ET_Folder < ET_Base
|
72
|
+
include ET_SoapGet
|
73
|
+
include ET_SoapCUD
|
74
|
+
def id
|
75
|
+
'DataFolder'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class ET_Email < ET_Base
|
80
|
+
include ET_SoapGet
|
81
|
+
include ET_SoapCUD
|
82
|
+
end
|
83
|
+
|
84
|
+
class ET_List < ET_Base
|
85
|
+
include ET_SoapGet
|
86
|
+
include ET_SoapCUD
|
87
|
+
|
88
|
+
class Subscriber < ET_Base
|
89
|
+
include ET_SoapGet
|
90
|
+
def id
|
91
|
+
'ListSubscriber'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
class ET_OpenEvent < ET_Base
|
97
|
+
include ET_SoapGet
|
98
|
+
end
|
99
|
+
|
100
|
+
class ET_SentEvent < ET_Base
|
101
|
+
include ET_SoapGet
|
102
|
+
end
|
103
|
+
|
104
|
+
class ET_Subscriber < ET_Base
|
105
|
+
include ET_SoapGet
|
106
|
+
include ET_SoapCUD
|
107
|
+
end
|
108
|
+
|
109
|
+
class ET_UnsubEvent < ET_Base
|
110
|
+
include ET_SoapGet
|
111
|
+
end
|
112
|
+
|
113
|
+
class ET_Campaign < ET_Base
|
114
|
+
include ET_RestGet
|
115
|
+
include ET_RestCUD
|
116
|
+
|
117
|
+
|
118
|
+
def properties
|
119
|
+
@properties ||= {}
|
120
|
+
@properties.merge! 'id' => '' unless @properties.include? 'id'
|
121
|
+
@properties
|
122
|
+
end
|
123
|
+
|
124
|
+
def id
|
125
|
+
"https://www.exacttargetapis.com/hub/v1/campaigns/%{id}"
|
126
|
+
end
|
127
|
+
|
128
|
+
class Asset < ET_Base
|
129
|
+
include ET_RestGet
|
130
|
+
include ET_RestCUD
|
131
|
+
|
132
|
+
def properties
|
133
|
+
@properties ||= {}
|
134
|
+
@properties.merge! 'assetId' => '' unless @properties.include? 'assetId'
|
135
|
+
@properties
|
136
|
+
end
|
137
|
+
|
138
|
+
def id
|
139
|
+
'https://www.exacttargetapis.com/hub/v1/campaigns/%{id}/assets/%{assetId}'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/lib/fuelsdk/rest.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module FuelSDK
|
2
|
+
module Rest
|
3
|
+
|
4
|
+
include FuelSDK::Targeting
|
5
|
+
|
6
|
+
def rest_client
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def normalize_keys obj
|
11
|
+
if obj and obj.is_a? Hash
|
12
|
+
obj.keys.each do |k|
|
13
|
+
obj[(k.to_sym rescue k) || k] = obj.delete(k)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
obj
|
17
|
+
end
|
18
|
+
|
19
|
+
def get_url_properties url, properties
|
20
|
+
url_property_names = url.scan(/(%{(.+?)})/).collect{|frmt, name| name}
|
21
|
+
url_properties = {}
|
22
|
+
properties.keys.each do |k|
|
23
|
+
if url_property_names.include? k
|
24
|
+
url_properties[k] = properties.delete(k)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
url_properties
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete_url url, url_properties
|
31
|
+
normalize_keys(url_properties)
|
32
|
+
url = url % url_properties if url_properties
|
33
|
+
url.end_with?('/') ? url.chop : url
|
34
|
+
rescue KeyError => ex
|
35
|
+
raise "#{ex.message} to complete #{url}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def parse_properties url, properties
|
39
|
+
url_properties = get_url_properties url, properties
|
40
|
+
url = complete_url url, url_properties
|
41
|
+
[url, properties]
|
42
|
+
end
|
43
|
+
|
44
|
+
def rest_get url, properties={}
|
45
|
+
url, properties = parse_properties url, properties
|
46
|
+
rest_request :get, url, {'params' => properties}
|
47
|
+
end
|
48
|
+
|
49
|
+
def rest_delete url, properties={}
|
50
|
+
url, properties = parse_properties url, properties
|
51
|
+
rest_request :delete, url
|
52
|
+
end
|
53
|
+
|
54
|
+
def rest_patch url, properties={}
|
55
|
+
url, payload = parse_properties url, properties
|
56
|
+
rest_request :patch, url, {'data' => payload,
|
57
|
+
'content_type' => 'application/json'}
|
58
|
+
end
|
59
|
+
|
60
|
+
def rest_post url, properties={}
|
61
|
+
url, payload = parse_properties url, properties
|
62
|
+
rest_request :post, url, {'data' => payload,
|
63
|
+
'content_type' => 'application/json'}
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def rest_request action, url, options={}
|
68
|
+
retried = false
|
69
|
+
begin
|
70
|
+
(options['params'] ||= {}).merge! 'access_token' => access_token
|
71
|
+
rsp = rest_client.send(action, url, options)
|
72
|
+
raise 'Unauthorized' if rsp.message == 'Unauthorized'
|
73
|
+
rescue
|
74
|
+
raise if retried
|
75
|
+
self.refresh! # ask for forgiveness not, permission
|
76
|
+
retried = true
|
77
|
+
retry
|
78
|
+
end
|
79
|
+
rsp
|
80
|
+
rescue
|
81
|
+
rsp
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|