fuelsdk 0.0.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 +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
|