exact_target_rest 0.1.1 → 0.2.2

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.
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