exact_target_rest 0.1.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fa84ba5f3df0a1fd9a95147ed0bfcc378160bed0
4
- data.tar.gz: b22021bcd6e34c42d3b678932a49c165edc4e349
3
+ metadata.gz: c619daa30b70dc6807b0e57e95de854633bc4328
4
+ data.tar.gz: 70f5940d63176afbc6550e3fef3983f081af6913
5
5
  SHA512:
6
- metadata.gz: a197fe64b7a0665b28e4d361f21282825fe9a77fb29a9d3c2507320b39abdd4e18cd46e9fc11b4d291bc1bdb0a432ac46f6789b580ff087ce840f45c7819d15c
7
- data.tar.gz: 3a486c16b1e098904fa4c02537070eafaeb2d6c509a57434401e5bbcf69153c087282a7a21a6db12a339fe4cb94739f1966db29fced5f125f2ffad77d0c43dc2
6
+ metadata.gz: c9b75e3fa94e08c19ce0f7d673e35fb9ada526ab47069e7286345a68ecaeada3d7ccdc27956855f047e7bbdaf6b78897efa189de35f614cb301cba9f0c3c2864
7
+ data.tar.gz: a6a468e7313dc448b20888e8f15250fff7a9936947b45a10f2db1b9921d61af277112b827b4219f0551e4d0fbba99d51ece0854b99f606e166d89fbec2928881
data/README.md CHANGED
@@ -34,22 +34,57 @@ auth = ExactTargetRest::Authorization.new(client_id, client_secret)
34
34
 
35
35
  ### TriggeredSend Activation
36
36
 
37
+ There are two ways to use.
38
+
39
+ The first calls `send_one` and receives DataExtensions as parameters:
40
+
37
41
  ```ruby
38
42
  external_key = 'My TriggeredSend External Key'
39
43
  ts = ExactTargetRest::TriggeredSend.new(auth, external_key)
40
44
  ts.send_one(email_address: 'uga@kabuga.com', an_attribute: 'XXX', another_attribute: 'YYY')
41
45
  ```
42
46
 
43
- **email_address** is mandatory, any other field will be put in the DataExtension (or List) associated with the TriggeredSend. You can also pass "subscriber_key" as parameter, if absent, it will use the value in "email_address" as default value.
44
-
45
- All other attributes will be converted to CamelCase (ExactTarget convention) before send. So, "an_attribute" and "another_attribute" would become "AnAttribute" and "AnotherAttribute".
47
+ **email_address** is mandatory, any other field will be put in the DataExtension (or List) associated with the TriggeredSend. You can also pass **subscriber_key** as parameter, if absent, it will use the value in **email_address** as default value.
46
48
 
47
- If you don't want this behavior, pass the flag "snake\_to\_camel: false" in the constructor:
49
+ In first example, with method `send_one`, all other attributes will be converted to CamelCase (ExactTarget convention) before send. So, **an_attribute** and **another_attribute** would become "AnAttribute" and "AnotherAttribute". If you don't want this behavior, pass the flag "snake\_to\_camel: false" in the constructor:
48
50
 
49
51
  ```ruby
50
52
  ts = ExactTargetRest::TriggeredSend.new(auth, external_key, snake_to_camel: false)
51
53
  ```
52
54
 
55
+ The second way helps in two situations:
56
+
57
+ - If you have DataExtension's keys with spaces or any unusual pattern
58
+
59
+ ```ruby
60
+ external_key = 'My TriggeredSend External Key'
61
+ ts = ExactTargetRest::TriggeredSend.new(auth, external_key)
62
+ ts.with_options(email_address: 'uga@kabuga.com', subscriber_attributes: { 'An Attribute' => 'XXX', 'Another_Attribute' => 'YYY' }).deliver
63
+ ```
64
+
65
+ - if you have to call the api asynchronously
66
+
67
+ ```ruby
68
+ def deliver
69
+ external_key = 'My TriggeredSend External Key'
70
+ triggered_send = ExactTargetRest::TriggeredSend.new(auth, external_key)
71
+ triggered_send.with_options(email_address: 'uga@kabuga.com')
72
+
73
+ Worker.perform_async(triggered_send.to_yaml)
74
+ end
75
+
76
+ class Worker
77
+ include Sidekiq::Worker
78
+
79
+ sidekiq_options queue: :exact_target_mailer
80
+
81
+ def perform(triggered_send)
82
+ YAML::load(triggered_send).deliver
83
+ end
84
+ end
85
+
86
+ ```
87
+
53
88
  ### DataExtension Upsert
54
89
 
55
90
  ```ruby
@@ -25,4 +25,6 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'rake', '~> 10.0'
26
26
  spec.add_development_dependency 'guard-rspec', '~> 4.5'
27
27
  spec.add_development_dependency 'pry', '~> 0.10'
28
+ spec.add_development_dependency 'pry-byebug', '~> 3.4.0'
29
+ spec.add_development_dependency 'webmock', '~> 2.1.0'
28
30
  end
@@ -21,20 +21,13 @@ module ExactTargetRest
21
21
  #
22
22
  # @yield [access_token] Block to be executed
23
23
  # @yieldparam access_token [String] Access token used to authorize a request
24
+ # @yieldparam expires_in [String] Time to token's expire
24
25
  def with_authorization
25
26
  authorize! unless authorized?
26
- tries = 1
27
- begin
28
- yield @access_token
29
- rescue NotAuthorizedError
30
- authorize!
31
- tries -= 1
32
- retry if tries >= 0
33
- end
34
-
27
+ yield @access_token
35
28
  end
36
29
 
37
- # Execute authorization and keeps an access token
30
+ # Execute authorization, keeps an access token and returns the result
38
31
  def authorize!
39
32
  resp = endpoint.post do |p|
40
33
  p.body = {clientId: @client_id,
@@ -43,14 +36,15 @@ module ExactTargetRest
43
36
  if resp.success?
44
37
  @access_token = resp.body['accessToken']
45
38
  @expires_in = Time.now + resp.body['expiresIn']
39
+ { access_token: @access_token, expires_in: @expires_in }
46
40
  else
47
- fail resp.body.inspect
41
+ fail NotAuthorizedError
48
42
  end
49
43
  end
50
44
 
51
45
  # Already authorized and NOT expired?
52
46
  def authorized?
53
- @access_token && @expires_in < Time.now
47
+ @access_token && @expires_in > Time.now
54
48
  end
55
49
 
56
50
  protected
@@ -63,4 +57,4 @@ module ExactTargetRest
63
57
  end
64
58
  end
65
59
  end
66
- end
60
+ end
@@ -42,4 +42,4 @@ module ExactTargetRest
42
42
  end
43
43
  end
44
44
  end
45
- end
45
+ end
@@ -12,28 +12,81 @@ module ExactTargetRest
12
12
  end
13
13
 
14
14
  # TriggeredSend for just one subscriber.
15
- #
16
- # @param email_address [String] Email to send.
15
+ # @param to_address [String] Email to send.
17
16
  # @param subscriber_key [String] SubscriberKey (it uses Email if not set).
18
17
  # @param data_extension_attributes [{Symbol => Object}] List of attributes (in snake_case)
19
18
  # that will be used in TriggeredSend and will be saved in related DataExtension
20
19
  # (in CamelCase).
21
- def send_one(email_address:, subscriber_key: email_address, ** data_extension_attributes)
20
+ def send_one(
21
+ email_address:,
22
+ subscriber_key: email_address,
23
+ ** data_extension_attributes
24
+ )
25
+ with_options(
26
+ email_address: email_address,
27
+ subscriber_key: subscriber_key,
28
+ subscriber_attributes: prepare_attributes(data_extension_attributes)
29
+ ).deliver
30
+ end
31
+
32
+ # Load attributes and return "self". To send using async methods.
33
+ # @param request_type [String] ASYNC or SYNC.
34
+ # @param to_address [String] Email to send.
35
+ # @param subscriber_key [String] SubscriberKey.
36
+ # => it uses Email if not set
37
+ # @param from_address [String] Sender email address.
38
+ # @param from_name [String] Sender name.
39
+ # @param subscriber_attributes [{String => String, ...}] List of attributes
40
+ # => Keys as Strings (when your ExactTarget's fields doesn't have a pattern)
41
+ def with_options(
42
+ request_type: "ASYNC",
43
+ email_address:,
44
+ subscriber_key: email_address,
45
+ from_address: "",
46
+ from_name: "",
47
+ subscriber_attributes: {}
48
+ )
49
+ @request_type = request_type
50
+ @email_address = email_address
51
+ @subscriber_key = subscriber_key
52
+ @from_address = from_address
53
+ @from_name = from_name
54
+ @subscriber_attributes = subscriber_attributes
55
+
56
+ self
57
+ end
58
+
59
+ # TriggeredSend with loaded attributes.
60
+ def deliver
61
+ tries ||= 1
22
62
  @authorization.with_authorization do |access_token|
23
63
  resp = endpoint.post do |p|
24
64
  p.url(format(TRIGGERED_SEND_PATH, URI.encode(@external_key)))
25
65
  p.headers['Authorization'] = "Bearer #{access_token}"
26
- p.body = {To: {
27
- Address: email_address,
28
- SubscriberKey: subscriber_key,
66
+ p.body = {
67
+ From: {
68
+ Address: @from_address,
69
+ Name: @from_name
70
+ },
71
+ To: {
72
+ Address: @email_address,
73
+ SubscriberKey: @subscriber_key,
29
74
  ContactAttributes: {
30
- SubscriberAttributes: prepare_attributes(data_extension_attributes)
75
+ SubscriberAttributes: @subscriber_attributes
31
76
  }
32
- }}
77
+ },
78
+ OPTIONS: {
79
+ RequestType: @request_type
80
+ }
81
+ }
33
82
  end
34
83
  raise NotAuthorizedError if resp.status == 401
35
84
  resp
36
85
  end
86
+ rescue NotAuthorizedError
87
+ tries -= 1
88
+ retry if tries >= 0
89
+ raise NotAuthorizedError
37
90
  end
38
91
 
39
92
  protected
@@ -50,4 +103,4 @@ module ExactTargetRest
50
103
  @snake_to_camel ? attributes.snake_to_camel : attributes
51
104
  end
52
105
  end
53
- end
106
+ end
@@ -1,3 +1,3 @@
1
1
  module ExactTargetRest
2
- VERSION = '0.1.1'
2
+ VERSION = '0.2.2'
3
3
  end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ describe Authorization do
4
+
5
+ let(:client_id) { "12345" }
6
+ let(:client_secret) { "Y9axRxR9bcvSW2cc0IwoWeq7" }
7
+ let(:expires_in) { 3600 }
8
+
9
+ let(:access_token) { "75sf4WWbwfr6HYd5URpC6KBk" }
10
+
11
+ subject do
12
+ described_class
13
+ end
14
+
15
+ before do
16
+ stub_requests
17
+ end
18
+
19
+ describe '#with_authorization' do
20
+ it "returns a valid authorization" do
21
+ subject.new(client_id, client_secret).with_authorization do |access_token|
22
+ expect(access_token).to eq access_token
23
+ end
24
+ end
25
+
26
+ it "returns Unauthorized" do
27
+ expect {
28
+ subject.new("invalid", client_secret).with_authorization
29
+ }.to raise_error NotAuthorizedError
30
+ end
31
+ end
32
+
33
+ describe '#authorize!' do
34
+ it "returns a valid authorization" do
35
+ auth = subject.new(client_id, client_secret).authorize!
36
+
37
+ expect(auth[:access_token]).to eq access_token
38
+ expect(auth[:expires_in]).not_to be_nil
39
+ end
40
+
41
+ it "returns Unauthorized" do
42
+ expect {
43
+ subject.new("invalid", client_secret).authorize!
44
+ }.to raise_error NotAuthorizedError
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def stub_requests
51
+ stub_request(:post, "https://auth.exacttargetapis.com/v1/requestToken").
52
+ with(
53
+ :body => "{\"clientId\":\"12345\",\"clientSecret\":\"Y9axRxR9bcvSW2cc0IwoWeq7\"}",
54
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}
55
+ ).
56
+ to_return(
57
+ headers: {"Content-Type"=> "application/json"},
58
+ body: %({"accessToken": "#{access_token}", "expiresIn": 3600}),
59
+ status: 200
60
+ )
61
+
62
+ stub_request(:any, ExactTargetRest::AUTH_URL).
63
+ with(
64
+ :body => "{\"clientId\":\"invalid\",\"clientSecret\":\"Y9axRxR9bcvSW2cc0IwoWeq7\"}",
65
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}
66
+ ).
67
+ to_return(
68
+ headers: {"Content-Type"=> "application/json"},
69
+ body: %({"message": "Unauthorized","errorcode": 1,"documentation": ""}),
70
+ status: 401
71
+ )
72
+ end
73
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+
3
+ describe TriggeredSend do
4
+
5
+ let(:external_key) { "12345" }
6
+ let(:access_token) { "Y9axRxR9bcvSW2cc0IwoWeq7" }
7
+ let(:expires_in) { 3600 }
8
+
9
+ subject do
10
+ authorization = instance_double("ExactTargetRest::Authorization")
11
+ allow(authorization).to receive(:with_authorization).and_yield(access_token)
12
+ described_class.new(authorization,external_key)
13
+ end
14
+
15
+ before do
16
+ stub_requests
17
+ end
18
+
19
+ describe '#send_one' do
20
+ it "sends a simple TriggeredSend" do
21
+ response = subject.send_one(email_address: "jake@oo.com")
22
+ expect(response.body["requestId"]).to eq "simple-response-id"
23
+ end
24
+
25
+ it "sends a TriggeredSend with DataExtension" do
26
+ response = subject.send_one(
27
+ email_address: "jake@oo.com",
28
+ city: "São Paulo",
29
+ zip: "04063-040"
30
+ )
31
+ expect(response.body["requestId"]).to eq "data-extension-response-id"
32
+ end
33
+ end
34
+
35
+ describe '#deliver' do
36
+ it "sends a simple TriggeredSend" do
37
+ response = subject.with_options(email_address: "jake@oo.com").deliver
38
+ expect(response.body["requestId"]).to eq "simple-response-id"
39
+ end
40
+
41
+ it "sends a TriggeredSend with DataExtension" do
42
+ response = subject.with_options(
43
+ email_address: "jake@oo.com",
44
+ subscriber_attributes: { City: "São Paulo", Zip: "04063-040" }
45
+ ).deliver
46
+ expect(response.body["requestId"]).to eq "data-extension-response-id"
47
+ end
48
+
49
+ it "sends a TriggeredSend with a DataExtension's key with spaces" do
50
+ response = subject.with_options(
51
+ email_address: "jake@oo.com",
52
+ subscriber_attributes: { "City" => "São Paulo", "Profile ID" => "42" }
53
+ ).deliver
54
+ expect(response.body["requestId"]).to eq "uncommon-key-response-id"
55
+ end
56
+
57
+ end
58
+
59
+ private
60
+
61
+ def stub_requests
62
+ stub_request(:any, ExactTargetRest::AUTH_URL).
63
+ to_return(
64
+ headers: {"Content-Type"=> "application/json"},
65
+ body: %({"accessToken": "75sf4WWbwfr6HYd5URpC6KBk", "expiresIn": 3600}),
66
+ status: 200
67
+ )
68
+
69
+ stub_request(:post, triggered_send_url).
70
+ with(
71
+ :body => "{\"From\":{\"Address\":\"\",\"Name\":\"\"},\"To\":{\"Address\":\"jake@oo.com\",\"SubscriberKey\":\"jake@oo.com\",\"ContactAttributes\":{\"SubscriberAttributes\":{\"City\":\"São Paulo\",\"Zip\":\"04063-040\"}}},\"OPTIONS\":{\"RequestType\":\"ASYNC\"}}",
72
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>"Bearer #{access_token}", 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}
73
+ ).
74
+ to_return(
75
+ headers: {"Content-Type"=> "application/json"},
76
+ body: stub_response("data-extension-response-id"),
77
+ status: 202
78
+ )
79
+
80
+ stub_request(:post, triggered_send_url).
81
+ with(
82
+ :body => "{\"From\":{\"Address\":\"\",\"Name\":\"\"},\"To\":{\"Address\":\"jake@oo.com\",\"SubscriberKey\":\"jake@oo.com\",\"ContactAttributes\":{\"SubscriberAttributes\":{\"City\":\"São Paulo\",\"Profile ID\":\"42\"}}},\"OPTIONS\":{\"RequestType\":\"ASYNC\"}}",
83
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>"Bearer #{access_token}", 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}
84
+ ).
85
+ to_return(
86
+ headers: {"Content-Type"=> "application/json"},
87
+ body: stub_response("uncommon-key-response-id"),
88
+ status: 202
89
+ )
90
+
91
+ stub_request(:post, triggered_send_url).
92
+ with(
93
+ :body => "{\"From\":{\"Address\":\"\",\"Name\":\"\"},\"To\":{\"Address\":\"jake@oo.com\",\"SubscriberKey\":\"jake@oo.com\",\"ContactAttributes\":{\"SubscriberAttributes\":{}}},\"OPTIONS\":{\"RequestType\":\"ASYNC\"}}",
94
+ :headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Authorization'=>"Bearer #{access_token}", 'Content-Type'=>'application/json', 'User-Agent'=>'Faraday v0.9.2'}
95
+ ).
96
+ to_return(
97
+ headers: {"Content-Type"=> "application/json"},
98
+ body: stub_response("simple-response-id"),
99
+ status: 202
100
+ )
101
+ end
102
+
103
+ def triggered_send_url
104
+ "#{TRIGGERED_SEND_URL}#{TRIGGERED_SEND_PATH}" % external_key
105
+ end
106
+
107
+ def stub_response(mockId)
108
+ %({
109
+ "requestId": "#{mockId}",
110
+ "responses": [ {
111
+ "recipientSendId": "#{mockId}",
112
+ "hasErrors": false,
113
+ "messages": ["Queued"]
114
+ }]
115
+ })
116
+ end
117
+
118
+
119
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,2 +1,4 @@
1
1
  require 'exact_target_rest'
2
+ require 'webmock/rspec'
3
+ require 'pry'
2
4
  include ExactTargetRest
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exact_target_rest
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ronie Uliana
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-08 00:00:00.000000000 Z
11
+ date: 2016-06-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.10'
97
+ - !ruby/object:Gem::Dependency
98
+ name: pry-byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 3.4.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 3.4.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 2.1.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 2.1.0
97
125
  description: Simple wrapper around ExactTarget REST API. It deals with authorization
98
126
  and with coding conventions (CamelCase vs snake_case)
99
127
  email:
@@ -104,7 +132,6 @@ extra_rdoc_files: []
104
132
  files:
105
133
  - ".gitignore"
106
134
  - ".ruby-gemset"
107
- - ".ruby-version"
108
135
  - Gemfile
109
136
  - Guardfile
110
137
  - LICENSE.txt
@@ -119,9 +146,11 @@ files:
119
146
  - lib/exact_target_rest/support/string_extension.rb
120
147
  - lib/exact_target_rest/triggered_send.rb
121
148
  - lib/exact_target_rest/version.rb
149
+ - spec/lib/exact_target_rest/authorization_spec.rb
122
150
  - spec/lib/exact_target_rest/support/data_ext_params_spec.rb
123
151
  - spec/lib/exact_target_rest/support/hash_extension_spec.rb
124
152
  - spec/lib/exact_target_rest/support/string_extension_spec.rb
153
+ - spec/lib/exact_target_rest/triggered_send_spec.rb
125
154
  - spec/spec_helper.rb
126
155
  homepage: ''
127
156
  licenses:
@@ -143,12 +172,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
143
172
  version: '0'
144
173
  requirements: []
145
174
  rubyforge_project:
146
- rubygems_version: 2.4.6
175
+ rubygems_version: 2.5.1
147
176
  signing_key:
148
177
  specification_version: 4
149
178
  summary: Simple wrapper around ExactTarget REST API
150
179
  test_files:
180
+ - spec/lib/exact_target_rest/authorization_spec.rb
151
181
  - spec/lib/exact_target_rest/support/data_ext_params_spec.rb
152
182
  - spec/lib/exact_target_rest/support/hash_extension_spec.rb
153
183
  - spec/lib/exact_target_rest/support/string_extension_spec.rb
184
+ - spec/lib/exact_target_rest/triggered_send_spec.rb
154
185
  - spec/spec_helper.rb
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- ruby-2.2