customerio 2.2.1 → 3.0.0

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