customerio 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,49 @@
1
+ require 'base64'
2
+
3
+ module Customerio
4
+ class SendEmailRequest
5
+ attr_reader :message
6
+
7
+ def initialize(opts)
8
+ @message = opts.delete_if { |field| invalid_field?(field) }
9
+ @message[:attachments] = {}
10
+ @message[:headers] = {}
11
+ end
12
+
13
+ def attach(name, data, encode: true)
14
+ raise "attachment #{name} already exists" if @message[:attachments].has_key?(name)
15
+ @message[:attachments][name] = encode ? Base64.strict_encode64(data) : data
16
+ end
17
+
18
+ private
19
+
20
+ REQUIRED_FIELDS = %i(to identifiers)
21
+
22
+ OPTIONAL_FIELDS = %i(
23
+ transactional_message_id
24
+ message_data
25
+ headers
26
+ preheader
27
+ from
28
+ reply_to
29
+ bcc
30
+ subject
31
+ body
32
+ plaintext_body
33
+ amp_body
34
+ fake_bcc
35
+ disable_message_retention
36
+ send_to_unsubscribed
37
+ tracked
38
+ queue_draft
39
+ )
40
+
41
+ def invalid_field?(field)
42
+ !REQUIRED_FIELDS.include?(field) && !OPTIONAL_FIELDS.include?(field)
43
+ end
44
+
45
+ def encode(data)
46
+ Base64.strict_encode64(data)
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Customerio
2
- VERSION = "2.2.1"
2
+ VERSION = "3.0.0"
3
3
  end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+ require 'multi_json'
3
+ require 'base64'
4
+ require 'tempfile'
5
+
6
+ describe Customerio::APIClient do
7
+ let(:app_key) { "appkey" }
8
+
9
+ let(:client) { Customerio::APIClient.new(app_key) }
10
+ let(:response) { double("Response", code: 200) }
11
+
12
+ def api_uri(path)
13
+ "https://api.customer.io#{path}"
14
+ end
15
+
16
+ def request_headers
17
+ { 'Authorization': "Bearer #{app_key}", 'Content-Type': 'application/json' }
18
+ end
19
+
20
+ def json(data)
21
+ MultiJson.dump(data)
22
+ end
23
+
24
+ describe "#send_email" do
25
+ it "sends a POST request to the /api/send/email path" do
26
+ req = Customerio::SendEmailRequest.new(
27
+ identifiers: {
28
+ id: 'c1',
29
+ },
30
+ transactional_message_id: 1,
31
+ )
32
+
33
+ stub_request(:post, api_uri('/v1/send/email'))
34
+ .with(headers: request_headers, body: req.message)
35
+ .to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})
36
+
37
+ client.send_email(req).should eq({ "delivery_id" => 1 })
38
+ end
39
+
40
+ it "handles validation failures (400)" do
41
+ req = Customerio::SendEmailRequest.new(
42
+ identifiers: {
43
+ id: 'c1',
44
+ },
45
+ transactional_message_id: 1,
46
+ )
47
+
48
+ err_json = { meta: { error: "example error" } }.to_json
49
+
50
+ stub_request(:post, api_uri('/v1/send/email'))
51
+ .with(headers: request_headers, body: req.message)
52
+ .to_return(status: 400, body: err_json, headers: {})
53
+
54
+ lambda { client.send_email(req) }.should(
55
+ raise_error(Customerio::InvalidResponse) { |error|
56
+ error.message.should eq "example error"
57
+ error.code.should eq "400"
58
+ }
59
+ )
60
+ end
61
+
62
+ it "handles other failures (5xx)" do
63
+ req = Customerio::SendEmailRequest.new(
64
+ identifiers: {
65
+ id: 'c1',
66
+ },
67
+ transactional_message_id: 1,
68
+ )
69
+
70
+ stub_request(:post, api_uri('/v1/send/email'))
71
+ .with(headers: request_headers, body: req.message)
72
+ .to_return(status: 500, body: "Server unavailable", headers: {})
73
+
74
+ lambda { client.send_email(req) }.should(
75
+ raise_error(Customerio::InvalidResponse) { |error|
76
+ error.message.should eq "Server unavailable"
77
+ error.code.should eq "500"
78
+ }
79
+ )
80
+ end
81
+
82
+ it "allows attaching file content without encoding" do
83
+ content = 'sample content'
84
+
85
+ req = Customerio::SendEmailRequest.new(
86
+ customer_id: 'c1',
87
+ transactional_message_id: 1,
88
+ )
89
+
90
+ req.attach('test', content, encode: false)
91
+ req.message[:attachments]['test'].should eq content
92
+
93
+ stub_request(:post, api_uri('/v1/send/email'))
94
+ .with(headers: request_headers, body: req.message)
95
+ .to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})
96
+
97
+ client.send_email(req)
98
+ end
99
+
100
+ it "allows attaching files with encoding (default)" do
101
+ content = 'sample content'
102
+
103
+ req = Customerio::SendEmailRequest.new(
104
+ customer_id: 'c1',
105
+ transactional_message_id: 1,
106
+ )
107
+
108
+ req.attach('test', content)
109
+ req.message[:attachments]['test'].should eq Base64.strict_encode64(content)
110
+
111
+ stub_request(:post, api_uri('/v1/send/email'))
112
+ .with(headers: request_headers, body: req.message)
113
+ .to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})
114
+
115
+ client.send_email(req)
116
+ end
117
+
118
+ it "raises error when attaching the same key again" do
119
+ req = Customerio::SendEmailRequest.new(
120
+ customer_id: 'c1',
121
+ transactional_message_id: 1,
122
+ )
123
+
124
+ req.attach('test', 'test-content')
125
+
126
+ lambda { req.attach('test', '') }.should raise_error(/attachment test already exists/)
127
+ req.message[:attachments].should eq({ "test" => Base64.strict_encode64("test-content") })
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+ require 'multi_json'
3
+ require 'base64'
4
+
5
+ describe Customerio::BaseClient do
6
+ let(:url) { "https://test.customer.io" }
7
+
8
+ let(:site_id) { "SITE_ID" }
9
+ let(:api_key) { "API_KEY" }
10
+ let(:track_client) { Customerio::BaseClient.new({ site_id: site_id, api_key: api_key }, { url: url }) }
11
+
12
+ let(:app_key) { "APP_KEY" }
13
+ let(:api_client) { Customerio::BaseClient.new({ app_key: app_key }, { url: url }) }
14
+
15
+ def api_uri(path)
16
+ "#{url}#{path}"
17
+ end
18
+
19
+ def track_client_request_headers
20
+ token = Base64.strict_encode64("#{site_id}:#{api_key}")
21
+ { 'Authorization': "Basic #{token}", 'Content-Type': 'application/json' }
22
+ end
23
+
24
+ def api_client_request_headers
25
+ { 'Authorization': "Bearer #{app_key}", 'Content-Type': 'application/json' }
26
+ end
27
+
28
+ describe "with a site ID and API key" do
29
+ it "uses the correct basic auth" do
30
+ stub_request(:put, api_uri('/some/path')).
31
+ with(headers: track_client_request_headers).
32
+ to_return(status: 200, body: "", headers: {})
33
+
34
+ track_client.request(:put, '/some/path', "")
35
+ end
36
+ end
37
+
38
+ describe "with an app key" do
39
+ it "uses the correct bearer token" do
40
+ stub_request(:put, api_uri('/some/path')).
41
+ with(headers: api_client_request_headers).
42
+ to_return(status: 200, body: "", headers: {})
43
+
44
+ api_client.request(:put, '/some/path', "")
45
+ end
46
+ end
47
+
48
+ describe "#verify_response" do
49
+ it "throws an error when the response isn't between 200 and 300" do
50
+ stub_request(:put, api_uri('/some/path')).
51
+ with(headers: api_client_request_headers).
52
+ to_return(status: 400, body: "", headers: {})
53
+
54
+ lambda { api_client.request_and_verify_response(:put, '/some/path', "") }.should(
55
+ raise_error(Customerio::InvalidResponse)
56
+ )
57
+ end
58
+
59
+ it "returns the response when the status is 200" do
60
+ stub_request(:put, api_uri('/some/path')).
61
+ with(headers: api_client_request_headers).
62
+ to_return(status: 200, body: "Test", headers: {})
63
+
64
+ api_client.request_and_verify_response(:put, '/some/path', "").body.should eq("Test")
65
+ end
66
+ end
67
+ end
@@ -1,39 +1,48 @@
1
1
  require 'spec_helper'
2
2
  require 'multi_json'
3
-
3
+ require 'base64'
4
4
 
5
5
  describe Customerio::Client do
6
- let(:client) { Customerio::Client.new("SITE_ID", "API_KEY", :json => false) }
7
- let(:response) { double("Response", :code => 200) }
6
+ let(:site_id) { "SITE_ID" }
7
+ let(:api_key) { "API_KEY" }
8
+
9
+ let(:client) { Customerio::Client.new(site_id, api_key) }
10
+ let(:response) { double("Response", code: 200) }
8
11
 
9
12
  def api_uri(path)
10
- "https://SITE_ID:API_KEY@track.customer.io#{path}"
13
+ "https://track.customer.io#{path}"
14
+ end
15
+
16
+ def request_headers
17
+ token = Base64.strict_encode64("#{site_id}:#{api_key}")
18
+ { 'Authorization': "Basic #{token}", 'Content-Type': 'application/json' }
11
19
  end
12
20
 
13
21
  def json(data)
14
22
  MultiJson.dump(data)
15
23
  end
16
24
 
17
- describe "json option" do
18
- let(:body) { { :id => 5, :name => "Bob" } }
25
+ it "uses json by default" do
26
+ body = { id: 5, name: "Bob" }
27
+ client = Customerio::Client.new("SITE_ID", "API_KEY")
19
28
 
20
- it "uses json by default" do
21
- client = Customerio::Client.new("SITE_ID", "API_KEY")
29
+ stub_request(:put, api_uri('/api/v1/customers/5')).
30
+ with(body: json(body),
31
+ headers: {'Content-Type'=>'application/json'}).
32
+ to_return(status: 200, body: "", headers: {})
22
33
 
23
- stub_request(:put, api_uri('/api/v1/customers/5')).
24
- with(:body => json(body),
25
- :headers => {'Content-Type'=>'application/json'}).
26
- to_return(:status => 200, :body => "", :headers => {})
34
+ client.identify(body)
35
+ end
27
36
 
28
- client.identify(body)
29
- end
37
+ describe "headers" do
38
+ let(:body) { { id: 1, token: :test } }
30
39
 
31
- it "allows disabling json" do
32
- client = Customerio::Client.new("SITE_ID", "API_KEY", :json => false)
40
+ it "sends the basic headers, base64 encoded with the request" do
41
+ client = Customerio::Client.new("SITE_ID", "API_KEY")
33
42
 
34
- stub_request(:put, api_uri('/api/v1/customers/5')).
35
- with(:body => { :id => "5", :name => "Bob" }).
36
- to_return(:status => 200, :body => "", :headers => {})
43
+ stub_request(:put, api_uri('/api/v1/customers/1')).
44
+ with(body: json(body), headers: request_headers).
45
+ to_return(status: 200, body: "", headers: {})
37
46
 
38
47
  client.identify(body)
39
48
  end
@@ -42,41 +51,54 @@ describe Customerio::Client do
42
51
  describe "#identify" do
43
52
  it "sends a PUT request to customer.io's customer API" do
44
53
  stub_request(:put, api_uri('/api/v1/customers/5')).
45
- with(:body => "id=5").
46
- to_return(:status => 200, :body => "", :headers => {})
54
+ with(body: json(id: "5")).
55
+ to_return(status: 200, body: "", headers: {})
56
+
57
+ client.identify(id: "5")
58
+ end
59
+
60
+ it "escapes customer IDs" do
61
+ stub_request(:put, api_uri('/api/v1/customers/5%20')).
62
+ with(body: json({ id: "5 " })).
63
+ to_return(status: 200, body: "", headers: {})
64
+
65
+ client.identify(id: "5 ")
47
66
 
48
- client.identify(:id => 5)
67
+ stub_request(:put, api_uri('/api/v1/customers/5%2F')).
68
+ with(body: { id: "5/" }).
69
+ to_return(status: 200, body: "", headers: {})
70
+ client.identify(id: "5/")
49
71
  end
50
72
 
51
73
  it "sends a PUT request to customer.io's customer API using json headers" do
52
- client = Customerio::Client.new("SITE_ID", "API_KEY", :json => true)
53
- body = { :id => 5, :name => "Bob" }
74
+ client = Customerio::Client.new("SITE_ID", "API_KEY", json: true)
75
+ body = { id: 5, name: "Bob" }
54
76
 
55
77
  stub_request(:put, api_uri('/api/v1/customers/5')).
56
- with(:body => json(body),
57
- :headers => {'Content-Type'=>'application/json'}).
58
- to_return(:status => 200, :body => "", :headers => {})
78
+ with(body: json(body),
79
+ headers: {'Content-Type'=>'application/json'}).
80
+ to_return(status: 200, body: "", headers: {})
59
81
 
60
82
  client.identify(body)
61
83
  end
62
84
 
63
85
  it "raises an error if PUT doesn't return a 2xx response code" do
64
86
  stub_request(:put, api_uri('/api/v1/customers/5')).
65
- with(:body => "id=5").
66
- to_return(:status => 500, :body => "", :headers => {})
87
+ with(body: json(id: 5)).
88
+ to_return(status: 500, body: "", headers: {})
67
89
 
68
- lambda { client.identify(:id => 5) }.should raise_error(Customerio::Client::InvalidResponse)
90
+ lambda { client.identify(id: 5) }.should raise_error(Customerio::InvalidResponse)
69
91
  end
70
92
 
71
93
  it "includes the HTTP response with raised errors" do
72
94
  stub_request(:put, api_uri('/api/v1/customers/5')).
73
- with(:body => "id=5").
74
- to_return(:status => 500, :body => "whatever", :headers => {})
95
+ with(body: json(id: 5)).
96
+ to_return(status: 500, body: "Server unavailable", headers: {})
75
97
 
76
- lambda { client.identify(:id => 5) }.should raise_error {|error|
77
- error.should be_a Customerio::Client::InvalidResponse
78
- error.response.code.should eq "500"
79
- error.response.body.should eq "whatever"
98
+ lambda { client.identify(id: 5) }.should raise_error {|error|
99
+ error.should be_a Customerio::InvalidResponse
100
+ error.code.should eq "500"
101
+ error.message.should eq "Server unavailable"
80
102
  }
81
103
  end
82
104
 
@@ -84,31 +106,32 @@ describe Customerio::Client do
84
106
  time = Time.now.to_i
85
107
 
86
108
  stub_request(:put, api_uri('/api/v1/customers/5')).with(
87
- :body => {
88
- :id => "5",
89
- :email => "customer@example.com",
90
- :created_at => time.to_s,
91
- :first_name => "Bob",
92
- :plan => "basic"
93
- }).to_return(:status => 200, :body => "", :headers => {})
109
+ body: json({
110
+ id: 5,
111
+ email: "customer@example.com",
112
+ created_at: time,
113
+ first_name: "Bob",
114
+ plan: "basic"
115
+ })).to_return(status: 200, body: "", headers: {})
94
116
 
95
117
  client.identify({
96
- :id => 5,
97
- :email => "customer@example.com",
98
- :created_at => time,
99
- :first_name => "Bob",
100
- :plan => "basic"
118
+ id: 5,
119
+ email: "customer@example.com",
120
+ created_at: time,
121
+ first_name: "Bob",
122
+ plan: "basic"
101
123
  })
102
124
  end
103
125
 
104
126
  it "requires an id attribute" do
105
- lambda { client.identify(:email => "customer@example.com") }.should raise_error(Customerio::Client::MissingIdAttributeError)
127
+ lambda { client.identify(email: "customer@example.com") }.should raise_error(Customerio::Client::MissingIdAttributeError)
128
+ lambda { client.identify(id: "") }.should raise_error(Customerio::Client::MissingIdAttributeError)
106
129
  end
107
130
 
108
131
  it 'should not raise errors when attribute keys are strings' do
109
132
  stub_request(:put, api_uri('/api/v1/customers/5')).
110
- with(:body => "id=5").
111
- to_return(:status => 200, :body => "", :headers => {})
133
+ with(body: json(id: 5)).
134
+ to_return(status: 200, body: "", headers: {})
112
135
 
113
136
  attributes = { "id" => 5 }
114
137
 
@@ -119,201 +142,237 @@ describe Customerio::Client do
119
142
  describe "#delete" do
120
143
  it "sends a DELETE request to the customer.io's event API" do
121
144
  stub_request(:delete, api_uri('/api/v1/customers/5')).
122
- to_return(:status => 200, :body => "", :headers => {})
145
+ to_return(status: 200, body: "", headers: {})
123
146
 
124
147
  client.delete(5)
125
148
  end
149
+
150
+ it "throws an error when customer_id is missing" do
151
+ stub_request(:put, /track.customer.io/)
152
+ .to_return(status: 200, body: "", headers: {})
153
+
154
+ lambda { client.delete(" ") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
155
+ end
156
+
157
+ it "escapes customer IDs" do
158
+ stub_request(:delete, api_uri('/api/v1/customers/5%20')).
159
+ to_return(status: 200, body: "", headers: {})
160
+
161
+ client.delete("5 ")
162
+ end
126
163
  end
127
164
 
128
165
  describe "#suppress" do
129
166
  it "sends a POST request to the customer.io's suppress API" do
130
167
  stub_request(:post, api_uri('/api/v1/customers/5/suppress')).
131
- to_return(:status => 200, :body => "", :headers => {})
168
+ to_return(status: 200, body: "", headers: {})
132
169
 
133
170
  client.suppress(5)
134
171
  end
172
+
173
+ it "throws an error when customer_id is missing" do
174
+ stub_request(:put, /track.customer.io/)
175
+ .to_return(status: 200, body: "", headers: {})
176
+
177
+ lambda { client.suppress(" ") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
178
+ end
135
179
  end
136
180
 
137
181
  describe "#unsuppress" do
138
182
  it "sends a POST request to the customer.io's unsuppress API" do
139
183
  stub_request(:post, api_uri('/api/v1/customers/5/unsuppress')).
140
- to_return(:status => 200, :body => "", :headers => {})
184
+ to_return(status: 200, body: "", headers: {})
141
185
 
142
186
  client.unsuppress(5)
143
187
  end
188
+
189
+ it "throws an error when customer_id is missing" do
190
+ stub_request(:put, /track.customer.io/)
191
+ .to_return(status: 200, body: "", headers: {})
192
+
193
+ lambda { client.suppress(" ") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
194
+ end
144
195
  end
145
196
 
146
197
  describe "#track" do
147
198
  it "raises an error if POST doesn't return a 2xx response code" do
148
199
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
149
- with(:body => "name=purchase").
150
- to_return(:status => 500, :body => "", :headers => {})
200
+ with(body: json(name: "purchase", data: {})).
201
+ to_return(status: 500, body: "", headers: {})
202
+
203
+ lambda { client.track(5, "purchase") }.should raise_error(Customerio::InvalidResponse)
204
+ end
205
+
206
+ it "throws an error when customer_id or event_name is missing" do
207
+ stub_request(:put, /track.customer.io/)
208
+ .to_return(status: 200, body: "", headers: {})
151
209
 
152
- lambda { client.track(5, "purchase") }.should raise_error(Customerio::Client::InvalidResponse)
210
+ lambda { client.track(" ", "test_event") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
211
+ lambda { client.track(5, " ") }.should raise_error(Customerio::Client::ParamError, "event_name must be a non-empty string")
153
212
  end
154
213
 
155
214
  it "uses the site_id and api key for basic auth and sends the event name" do
156
215
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
157
- with(:body => "name=purchase").
158
- to_return(:status => 200, :body => "", :headers => {})
216
+ with(body: json(name: "purchase", data: {})).
217
+ to_return(status: 200, body: "", headers: {})
159
218
 
160
219
  client.track(5, "purchase")
161
220
  end
162
221
 
163
222
  it "sends any optional event attributes" do
164
223
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
165
- with(:body => {
166
- :name => "purchase",
167
- :data => {
168
- :type => "socks",
169
- :price => "13.99"
224
+ with(body: json({
225
+ name: "purchase",
226
+ data: {
227
+ type: "socks",
228
+ price: "13.99"
170
229
  }
171
- }).
172
- to_return(:status => 200, :body => "", :headers => {})
230
+ })).
231
+ to_return(status: 200, body: "", headers: {})
173
232
 
174
- client.track(5, "purchase", :type => "socks", :price => "13.99")
233
+ client.track(5, "purchase", type: "socks", price: "13.99")
175
234
  end
176
235
 
177
236
  it "copes with arrays" do
178
237
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
179
- with(:body => {
180
- :name => "event",
181
- :data => {
182
- :things => ["a", "b", "c"]
238
+ with(body: {
239
+ name: "event",
240
+ data: {
241
+ things: ["a", "b", "c"]
183
242
  }
184
243
  }).
185
- to_return(:status => 200, :body => "", :headers => {})
244
+ to_return(status: 200, body: "", headers: {})
186
245
 
187
- client.track(5, "event", :things => ["a", "b", "c"])
246
+ client.track(5, "event", things: ["a", "b", "c"])
188
247
  end
189
248
 
190
249
  it "copes with hashes" do
191
250
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
192
- with(:body => {
193
- :name => "event",
194
- :data => {
195
- :stuff => { :a => "b" }
251
+ with(body: {
252
+ name: "event",
253
+ data: {
254
+ stuff: { a: "b" }
196
255
  }
197
256
  }).
198
- to_return(:status => 200, :body => "", :headers => {})
257
+ to_return(status: 200, body: "", headers: {})
199
258
 
200
- client.track(5, "event", :stuff => { :a => "b" })
259
+ client.track(5, "event", stuff: { a: "b" })
201
260
  end
202
261
 
203
262
  it "sends a POST request as json using json headers" do
204
- client = Customerio::Client.new("SITE_ID", "API_KEY", :json => true)
205
- data = { :type => "socks", :price => "13.99" }
206
- body = { :name => "purchase", :data => data }
263
+ client = Customerio::Client.new("SITE_ID", "API_KEY", json: true)
264
+ data = { type: "socks", price: "13.99" }
265
+ body = { name: "purchase", data: data }
207
266
 
208
267
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
209
- with(:body => json(body),
210
- :headers => {'Content-Type'=>'application/json'}).
211
- to_return(:status => 200, :body => "", :headers => {})
268
+ with(body: json(body),
269
+ headers: {'Content-Type'=>'application/json'}).
270
+ to_return(status: 200, body: "", headers: {})
212
271
 
213
272
  client.track(5, "purchase", data)
214
273
  end
215
274
 
216
275
  it "allows sending of a timestamp" do
217
276
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
218
- with(:body => {
219
- :name => "purchase",
220
- :data => {
221
- :type => "socks",
222
- :price => "13.99",
223
- :timestamp => "1561231234"
277
+ with(body: json({
278
+ name: "purchase",
279
+ data: {
280
+ type: "socks",
281
+ price: "13.99",
282
+ timestamp: 1561231234
224
283
  },
225
- :timestamp => "1561231234"
226
- }).
227
- to_return(:status => 200, :body => "", :headers => {})
284
+ timestamp: 1561231234
285
+ })).
286
+ to_return(status: 200, body: "", headers: {})
228
287
 
229
- client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => 1561231234)
288
+ client.track(5, "purchase", type: "socks", price: "13.99", timestamp: 1561231234)
230
289
  end
231
290
 
232
291
  it "doesn't send timestamp if timestamp is in milliseconds" do
233
292
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
234
- with(:body => {
235
- :name => "purchase",
236
- :data => {
237
- :type => "socks",
238
- :price => "13.99",
239
- :timestamp => "1561231234000"
293
+ with(body: json({
294
+ name: "purchase",
295
+ data: {
296
+ type: "socks",
297
+ price: "13.99",
298
+ timestamp: 1561231234000
240
299
  }
241
- }).
242
- to_return(:status => 200, :body => "", :headers => {})
300
+ })).
301
+ to_return(status: 200, body: "", headers: {})
243
302
 
244
- client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => 1561231234000)
303
+ client.track(5, "purchase", type: "socks", price: "13.99", timestamp: 1561231234000)
245
304
  end
246
305
 
247
306
  it "doesn't send timestamp if timestamp is a date" do
248
307
  date = Time.now
249
308
 
250
309
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
251
- with(:body => {
252
- :name => "purchase",
253
- :data => {
254
- :type => "socks",
255
- :price => "13.99",
256
- :timestamp => Time.now.to_s
310
+ with(body: {
311
+ name: "purchase",
312
+ data: {
313
+ type: "socks",
314
+ price: "13.99",
315
+ timestamp: Time.now.to_s
257
316
  }
258
317
  }).
259
- to_return(:status => 200, :body => "", :headers => {})
318
+ to_return(status: 200, body: "", headers: {})
260
319
 
261
- client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => date)
320
+ client.track(5, "purchase", type: "socks", price: "13.99", timestamp: date)
262
321
  end
263
322
 
264
323
  it "doesn't send timestamp if timestamp isn't an integer" do
265
324
  stub_request(:post, api_uri('/api/v1/customers/5/events')).
266
- with(:body => {
267
- :name => "purchase",
268
- :data => {
269
- :type => "socks",
270
- :price => "13.99",
271
- :timestamp => "Hello world"
325
+ with(body: json({
326
+ name: "purchase",
327
+ data: {
328
+ type: "socks",
329
+ price: "13.99",
330
+ timestamp: "Hello world"
272
331
  }
273
- }).
332
+ })).
274
333
 
275
- to_return(:status => 200, :body => "", :headers => {})
334
+ to_return(status: 200, body: "", headers: {})
276
335
 
277
- client.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp => "Hello world")
336
+ client.track(5, "purchase", type: "socks", price: "13.99", timestamp: "Hello world")
278
337
  end
279
338
 
280
339
  context "tracking an anonymous event" do
281
340
  it "sends a POST request to the customer.io's anonymous event API" do
282
341
  stub_request(:post, api_uri('/api/v1/events')).
283
- with(:body => "name=purchase").
284
- to_return(:status => 200, :body => "", :headers => {})
342
+ with(body: json({ name: "purchase", data: {} })).
343
+ to_return(status: 200, body: "", headers: {})
285
344
 
286
- client.track("purchase")
345
+ client.anonymous_track("purchase")
287
346
  end
288
347
 
289
348
  it "sends any optional event attributes" do
290
349
  stub_request(:post, api_uri('/api/v1/events')).
291
- with(:body => {
292
- :name => "purchase",
293
- :data => {
294
- :type => "socks",
295
- :price => "13.99"
350
+ with(body: json({
351
+ name: "purchase",
352
+ data: {
353
+ type: "socks",
354
+ price: "13.99"
296
355
  }
297
- }).
298
- to_return(:status => 200, :body => "", :headers => {})
356
+ })).
357
+ to_return(status: 200, body: "", headers: {})
299
358
 
300
- client.track("purchase", :type => "socks", :price => "13.99")
359
+ client.anonymous_track("purchase", type: "socks", price: "13.99")
301
360
  end
302
361
 
303
362
  it "allows sending of a timestamp" do
304
363
  stub_request(:post, api_uri('/api/v1/events')).
305
- with(:body => {
306
- :name => "purchase",
307
- :data => {
308
- :type => "socks",
309
- :price => "13.99",
310
- :timestamp => "1561231234"
364
+ with(body: json({
365
+ name: "purchase",
366
+ data: {
367
+ type: "socks",
368
+ price: "13.99",
369
+ timestamp: 1561231234
311
370
  },
312
- :timestamp => "1561231234"
313
- }).
314
- to_return(:status => 200, :body => "", :headers => {})
371
+ timestamp: 1561231234
372
+ })).
373
+ to_return(status: 200, body: "", headers: {})
315
374
 
316
- client.track("purchase", :type => "socks", :price => "13.99", :timestamp => 1561231234)
375
+ client.anonymous_track("purchase", type: "socks", price: "13.99", timestamp: 1561231234)
317
376
  end
318
377
  end
319
378
  end
@@ -321,55 +380,62 @@ describe Customerio::Client do
321
380
  describe "#anonymous_track" do
322
381
  it "raises an error if POST doesn't return a 2xx response code" do
323
382
  stub_request(:post, api_uri('/api/v1/events')).
324
- with(:body => "name=purchase").
325
- to_return(:status => 500, :body => "", :headers => {})
383
+ with(body: json(name: "purchase", data: {})).
384
+ to_return(status: 500, body: "", headers: {})
326
385
 
327
- lambda { client.anonymous_track("purchase") }.should raise_error(Customerio::Client::InvalidResponse)
386
+ lambda { client.anonymous_track("purchase") }.should raise_error(Customerio::InvalidResponse)
387
+ end
388
+
389
+ it "throws an error when event_name is missing" do
390
+ stub_request(:put, /track.customer.io/)
391
+ .to_return(status: 200, body: "", headers: {})
392
+
393
+ lambda { client.anonymous_track(" ") }.should raise_error(Customerio::Client::ParamError, "event_name must be a non-empty string")
328
394
  end
329
395
 
330
396
  it "uses the site_id and api key for basic auth and sends the event name" do
331
397
  stub_request(:post, api_uri('/api/v1/events')).
332
- with(:body => "name=purchase").
333
- to_return(:status => 200, :body => "", :headers => {})
398
+ with(body: json(name: "purchase", data: {})).
399
+ to_return(status: 200, body: "", headers: {})
334
400
 
335
401
  client.anonymous_track("purchase")
336
402
  end
337
403
 
338
404
  it "sends any optional event attributes" do
339
405
  stub_request(:post, api_uri('/api/v1/events')).
340
- with(:body => {
341
- :name => "purchase",
342
- :data => {
343
- :type => "socks",
344
- :price => "27.99"
406
+ with(body: {
407
+ name: "purchase",
408
+ data: {
409
+ type: "socks",
410
+ price: "27.99"
345
411
  },
346
412
  }).
347
413
 
348
- to_return(:status => 200, :body => "", :headers => {})
414
+ to_return(status: 200, body: "", headers: {})
349
415
 
350
- client.anonymous_track("purchase", :type => "socks", :price => "27.99")
416
+ client.anonymous_track("purchase", type: "socks", price: "27.99")
351
417
  end
352
418
 
353
419
  it "allows sending of a timestamp" do
354
420
  stub_request(:post, api_uri('/api/v1/events')).
355
- with(:body => {
356
- :name => "purchase",
357
- :data => {
358
- :type => "socks",
359
- :price => "27.99",
360
- :timestamp => "1561235678"
421
+ with(body: json({
422
+ name: "purchase",
423
+ data: {
424
+ type: "socks",
425
+ price: "27.99",
426
+ timestamp: 1561235678
361
427
  },
362
- :timestamp => "1561235678"
363
- }).
428
+ timestamp: 1561235678
429
+ })).
364
430
 
365
- to_return(:status => 200, :body => "", :headers => {})
431
+ to_return(status: 200, body: "", headers: {})
366
432
 
367
- client.anonymous_track("purchase", :type => "socks", :price => "27.99", :timestamp => 1561235678)
433
+ client.anonymous_track("purchase", type: "socks", price: "27.99", timestamp: 1561235678)
368
434
  end
369
435
 
370
436
  context "too many arguments are passed" do
371
437
  it "throws an error" do
372
- lambda { client.anonymous_track("purchase", "text", :type => "socks", :price => "27.99") }.should raise_error(ArgumentError)
438
+ lambda { client.anonymous_track("purchase", "text", type: "socks", price: "27.99") }.should raise_error(ArgumentError)
373
439
  end
374
440
  end
375
441
  end
@@ -377,109 +443,63 @@ describe Customerio::Client do
377
443
  describe "#devices" do
378
444
  it "allows for the creation of a new device" do
379
445
  stub_request(:put, api_uri('/api/v1/customers/5/devices')).
380
- to_return(:status => 200, :body => "", :headers => {})
446
+ to_return(status: 200, body: "", headers: {})
381
447
 
382
- client.add_device(5, "androidDeviceID", "ios", {:last_used=>1561235678})
448
+ client.add_device(5, "androidDeviceID", "ios", {last_used: 1561235678})
383
449
  client.add_device(5, "iosDeviceID", "android")
384
450
  end
385
451
  it "requires a valid customer_id when creating" do
386
452
  stub_request(:put, api_uri('/api/v1/customers/5/devices')).
387
- to_return(:status => 200, :body => "", :headers => {})
453
+ to_return(status: 200, body: "", headers: {})
388
454
 
389
455
  lambda { client.add_device("", "ios", "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
390
- lambda { client.add_device(nil, "ios", "myDeviceID", {:last_used=>1561235678}) }.should raise_error(Customerio::Client::ParamError)
456
+ lambda { client.add_device(nil, "ios", "myDeviceID", {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
391
457
  end
392
458
  it "requires a valid token when creating" do
393
459
  stub_request(:put, api_uri('/api/v1/customers/5/devices')).
394
- to_return(:status => 200, :body => "", :headers => {})
460
+ to_return(status: 200, body: "", headers: {})
395
461
 
396
462
  lambda { client.add_device(5, "", "ios") }.should raise_error(Customerio::Client::ParamError)
397
- lambda { client.add_device(5, nil, "ios", {:last_used=>1561235678}) }.should raise_error(Customerio::Client::ParamError)
463
+ lambda { client.add_device(5, nil, "ios", {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
398
464
  end
399
465
  it "requires a valid platform when creating" do
400
466
  stub_request(:put, api_uri('/api/v1/customers/5/devices')).
401
- to_return(:status => 200, :body => "", :headers => {})
467
+ to_return(status: 200, body: "", headers: {})
402
468
 
403
469
  lambda { client.add_device(5, "token", "") }.should raise_error(Customerio::Client::ParamError)
404
- lambda { client.add_device(5, "toke", nil, {:last_used=>1561235678}) }.should raise_error(Customerio::Client::ParamError)
470
+ lambda { client.add_device(5, "toke", nil, {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
405
471
  end
406
472
  it "accepts a nil data param" do
407
473
  stub_request(:put, api_uri('/api/v1/customers/5/devices')).
408
- to_return(:status => 200, :body => "", :headers => {})
474
+ to_return(status: 200, body: "", headers: {})
409
475
 
410
476
  client.add_device(5, "ios", "myDeviceID", nil)
411
477
  end
412
478
  it "fails on invalid data param" do
413
479
  stub_request(:put, api_uri('/api/v1/customers/5/devices')).
414
- to_return(:status => 200, :body => "", :headers => {})
480
+ to_return(status: 200, body: "", headers: {})
415
481
 
416
482
  lambda { client.add_device(5, "ios", "myDeviceID", 1000) }.should raise_error(Customerio::Client::ParamError)
417
483
  end
418
484
  it "supports deletion of devices by token" do
419
485
  stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
420
- to_return(:status => 200, :body => "", :headers => {})
486
+ to_return(status: 200, body: "", headers: {})
421
487
 
422
488
  client.delete_device(5, "myDeviceID")
423
489
  end
424
490
  it "requires a valid customer_id when deleting" do
425
491
  stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
426
- to_return(:status => 200, :body => "", :headers => {})
492
+ to_return(status: 200, body: "", headers: {})
427
493
 
428
494
  lambda { client.delete_device("", "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
429
495
  lambda { client.delete_device(nil, "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
430
496
  end
431
497
  it "requires a valid device_id when deleting" do
432
498
  stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
433
- to_return(:status => 200, :body => "", :headers => {})
499
+ to_return(status: 200, body: "", headers: {})
434
500
 
435
501
  lambda { client.delete_device(5, "") }.should raise_error(Customerio::Client::ParamError)
436
502
  lambda { client.delete_device(5, nil) }.should raise_error(Customerio::Client::ParamError)
437
503
  end
438
504
  end
439
-
440
- describe "#manual_segments" do
441
-
442
- client = Customerio::Client.new("SITE_ID", "API_KEY", :json=>true)
443
-
444
- it "allows adding customers to a manual segment" do
445
- stub_request(:post, api_uri('/api/v1/segments/1/add_customers')).to_return(:status => 200, :body => "", :headers => {})
446
-
447
- client.add_to_segment(1, ["customer1", "customer2", "customer3"])
448
- end
449
- it "requires a valid segment id when adding customers" do
450
- stub_request(:post, api_uri('/api/v1/segments/1/add_customers')).to_return(:status => 200, :body => "", :headers => {})
451
-
452
- lambda { client.add_to_segment("not_valid", ["customer1", "customer2", "customer3"]).should raise_error(Customerio::Client::ParamError) }
453
- end
454
- it "requires a valid customer list when adding customers" do
455
- stub_request(:post, api_uri('/api/v1/segments/1/add_customers')).to_return(:status => 200, :body => "", :headers => {})
456
-
457
- lambda { client.add_to_segment(1, "not_valid").should raise_error(Customerio::Client::ParamError) }
458
- end
459
- it "coerces non-string values to strings when adding customers" do
460
- stub_request(:post, api_uri('/api/v1/segments/1/add_customers')).with(:body=>json({:ids=>["1", "2", "3"]})).to_return(:status => 200, :body => "", :headers => {})
461
-
462
- client.add_to_segment(1, [1, 2, 3])
463
- end
464
- it "allows removing customers from a manual segment" do
465
- stub_request(:post, api_uri('/api/v1/segments/1/remove_customers')).to_return(:status => 200, :body => "", :headers => {})
466
-
467
- client.remove_from_segment(1, ["customer1", "customer2", "customer3"])
468
- end
469
- it "requires a valid segment id when removing customers" do
470
- stub_request(:post, api_uri('/api/v1/segments/1/remove_customers')).to_return(:status => 200, :body => "", :headers => {})
471
-
472
- lambda { client.remove_from_segment("not_valid", ["customer1", "customer2", "customer3"]).should raise_error(Customerio::Client::ParamError) }
473
- end
474
- it "requires a valid customer list when removing customers" do
475
- stub_request(:post, api_uri('/api/v1/segments/1/remove_customers')).to_return(:status => 200, :body => "", :headers => {})
476
-
477
- lambda { client.remove_from_segment(1, "not_valid").should raise_error(Customerio::Client::ParamError) }
478
- end
479
- it "coerces non-string values to strings when removing customers" do
480
- stub_request(:post, api_uri('/api/v1/segments/1/remove_customers')).with(:body=>json({:ids=>["1", "2", "3"]})).to_return(:status => 200, :body => "", :headers => {})
481
-
482
- client.remove_from_segment(1, [1, 2, 3])
483
- end
484
- end
485
505
  end