customerio 2.0.0 → 3.1.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.
- checksums.yaml +5 -13
- data/.circleci/config.yml +61 -0
- data/.github/workflows/main.yml +30 -0
- data/CHANGELOG.markdown +100 -49
- data/README.md +67 -9
- data/customerio.gemspec +3 -2
- data/lib/customerio.rb +4 -0
- data/lib/customerio/api.rb +35 -0
- data/lib/customerio/base_client.rb +87 -0
- data/lib/customerio/client.rb +55 -106
- data/lib/customerio/regions.rb +11 -0
- data/lib/customerio/requests/send_email_request.rb +49 -0
- data/lib/customerio/version.rb +1 -1
- data/spec/api_client_spec.rb +172 -0
- data/spec/base_client_spec.rb +67 -0
- data/spec/client_spec.rb +303 -175
- data/spec/spec_helper.rb +2 -2
- metadata +43 -34
- data/.travis.yml +0 -9
@@ -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
|
data/spec/client_spec.rb
CHANGED
@@ -1,39 +1,92 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'multi_json'
|
3
|
-
|
3
|
+
require 'base64'
|
4
4
|
|
5
5
|
describe Customerio::Client do
|
6
|
-
let(:
|
7
|
-
let(:
|
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://
|
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
|
-
|
18
|
-
|
25
|
+
it "the base client is initialised with the correct values when no region is passed in" do
|
26
|
+
site_id = "SITE_ID"
|
27
|
+
api_key = "API_KEY"
|
28
|
+
|
29
|
+
expect(Customerio::BaseClient).to(
|
30
|
+
receive(:new)
|
31
|
+
.with(
|
32
|
+
{ site_id: site_id, api_key: api_key },
|
33
|
+
{
|
34
|
+
region: Customerio::Regions::US,
|
35
|
+
url: Customerio::Regions::US.track_url
|
36
|
+
}
|
37
|
+
)
|
38
|
+
)
|
19
39
|
|
20
|
-
|
21
|
-
|
40
|
+
client = Customerio::Client.new(site_id, api_key)
|
41
|
+
end
|
22
42
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
43
|
+
it "raises an error when an incorrect region is passed in" do
|
44
|
+
expect {
|
45
|
+
Customerio::Client.new("siteid", "apikey", region: :au)
|
46
|
+
}.to raise_error /region must be an instance of Customerio::Regions::Region/
|
47
|
+
end
|
27
48
|
|
28
|
-
|
49
|
+
[Customerio::Regions::US, Customerio::Regions::EU].each do |region|
|
50
|
+
it "the base client is initialised with the correct values when the region \"#{region}\" is passed in" do
|
51
|
+
site_id = "SITE_ID"
|
52
|
+
api_key = "API_KEY"
|
53
|
+
|
54
|
+
expect(Customerio::BaseClient).to(
|
55
|
+
receive(:new)
|
56
|
+
.with(
|
57
|
+
{ site_id: site_id, api_key: api_key },
|
58
|
+
{
|
59
|
+
region: region,
|
60
|
+
url: region.track_url
|
61
|
+
}
|
62
|
+
)
|
63
|
+
)
|
64
|
+
|
65
|
+
client = Customerio::Client.new(site_id, api_key, { region: region })
|
29
66
|
end
|
67
|
+
end
|
30
68
|
|
31
|
-
|
32
|
-
|
69
|
+
it "uses json by default" do
|
70
|
+
body = { id: 5, name: "Bob" }
|
71
|
+
client = Customerio::Client.new("SITE_ID", "API_KEY")
|
33
72
|
|
34
|
-
|
35
|
-
|
36
|
-
|
73
|
+
stub_request(:put, api_uri('/api/v1/customers/5')).
|
74
|
+
with(body: json(body),
|
75
|
+
headers: {'Content-Type'=>'application/json'}).
|
76
|
+
to_return(status: 200, body: "", headers: {})
|
77
|
+
|
78
|
+
client.identify(body)
|
79
|
+
end
|
80
|
+
|
81
|
+
describe "headers" do
|
82
|
+
let(:body) { { id: 1, token: :test } }
|
83
|
+
|
84
|
+
it "sends the basic headers, base64 encoded with the request" do
|
85
|
+
client = Customerio::Client.new("SITE_ID", "API_KEY")
|
86
|
+
|
87
|
+
stub_request(:put, api_uri('/api/v1/customers/1')).
|
88
|
+
with(body: json(body), headers: request_headers).
|
89
|
+
to_return(status: 200, body: "", headers: {})
|
37
90
|
|
38
91
|
client.identify(body)
|
39
92
|
end
|
@@ -42,41 +95,54 @@ describe Customerio::Client do
|
|
42
95
|
describe "#identify" do
|
43
96
|
it "sends a PUT request to customer.io's customer API" do
|
44
97
|
stub_request(:put, api_uri('/api/v1/customers/5')).
|
45
|
-
with(:
|
46
|
-
to_return(:
|
98
|
+
with(body: json(id: "5")).
|
99
|
+
to_return(status: 200, body: "", headers: {})
|
100
|
+
|
101
|
+
client.identify(id: "5")
|
102
|
+
end
|
47
103
|
|
48
|
-
|
104
|
+
it "escapes customer IDs" do
|
105
|
+
stub_request(:put, api_uri('/api/v1/customers/5%20')).
|
106
|
+
with(body: json({ id: "5 " })).
|
107
|
+
to_return(status: 200, body: "", headers: {})
|
108
|
+
|
109
|
+
client.identify(id: "5 ")
|
110
|
+
|
111
|
+
stub_request(:put, api_uri('/api/v1/customers/5%2F')).
|
112
|
+
with(body: { id: "5/" }).
|
113
|
+
to_return(status: 200, body: "", headers: {})
|
114
|
+
client.identify(id: "5/")
|
49
115
|
end
|
50
116
|
|
51
117
|
it "sends a PUT request to customer.io's customer API using json headers" do
|
52
|
-
client = Customerio::Client.new("SITE_ID", "API_KEY", :
|
53
|
-
body = { :
|
118
|
+
client = Customerio::Client.new("SITE_ID", "API_KEY", json: true)
|
119
|
+
body = { id: 5, name: "Bob" }
|
54
120
|
|
55
121
|
stub_request(:put, api_uri('/api/v1/customers/5')).
|
56
|
-
with(:
|
57
|
-
:
|
58
|
-
to_return(:
|
122
|
+
with(body: json(body),
|
123
|
+
headers: {'Content-Type'=>'application/json'}).
|
124
|
+
to_return(status: 200, body: "", headers: {})
|
59
125
|
|
60
126
|
client.identify(body)
|
61
127
|
end
|
62
128
|
|
63
129
|
it "raises an error if PUT doesn't return a 2xx response code" do
|
64
130
|
stub_request(:put, api_uri('/api/v1/customers/5')).
|
65
|
-
with(:
|
66
|
-
to_return(:
|
131
|
+
with(body: json(id: 5)).
|
132
|
+
to_return(status: 500, body: "", headers: {})
|
67
133
|
|
68
|
-
lambda { client.identify(:
|
134
|
+
lambda { client.identify(id: 5) }.should raise_error(Customerio::InvalidResponse)
|
69
135
|
end
|
70
136
|
|
71
137
|
it "includes the HTTP response with raised errors" do
|
72
138
|
stub_request(:put, api_uri('/api/v1/customers/5')).
|
73
|
-
with(:
|
74
|
-
to_return(:
|
139
|
+
with(body: json(id: 5)).
|
140
|
+
to_return(status: 500, body: "Server unavailable", headers: {})
|
75
141
|
|
76
|
-
lambda { client.identify(:
|
77
|
-
error.should be_a Customerio::
|
78
|
-
error.
|
79
|
-
error.
|
142
|
+
lambda { client.identify(id: 5) }.should raise_error {|error|
|
143
|
+
error.should be_a Customerio::InvalidResponse
|
144
|
+
error.code.should eq "500"
|
145
|
+
error.message.should eq "Server unavailable"
|
80
146
|
}
|
81
147
|
end
|
82
148
|
|
@@ -84,31 +150,32 @@ describe Customerio::Client do
|
|
84
150
|
time = Time.now.to_i
|
85
151
|
|
86
152
|
stub_request(:put, api_uri('/api/v1/customers/5')).with(
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
92
|
-
:
|
93
|
-
}).to_return(:
|
153
|
+
body: json({
|
154
|
+
id: 5,
|
155
|
+
email: "customer@example.com",
|
156
|
+
created_at: time,
|
157
|
+
first_name: "Bob",
|
158
|
+
plan: "basic"
|
159
|
+
})).to_return(status: 200, body: "", headers: {})
|
94
160
|
|
95
161
|
client.identify({
|
96
|
-
:
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:
|
162
|
+
id: 5,
|
163
|
+
email: "customer@example.com",
|
164
|
+
created_at: time,
|
165
|
+
first_name: "Bob",
|
166
|
+
plan: "basic"
|
101
167
|
})
|
102
168
|
end
|
103
169
|
|
104
170
|
it "requires an id attribute" do
|
105
|
-
lambda { client.identify(:
|
171
|
+
lambda { client.identify(email: "customer@example.com") }.should raise_error(Customerio::Client::MissingIdAttributeError)
|
172
|
+
lambda { client.identify(id: "") }.should raise_error(Customerio::Client::MissingIdAttributeError)
|
106
173
|
end
|
107
174
|
|
108
175
|
it 'should not raise errors when attribute keys are strings' do
|
109
176
|
stub_request(:put, api_uri('/api/v1/customers/5')).
|
110
|
-
with(:
|
111
|
-
to_return(:
|
177
|
+
with(body: json(id: 5)).
|
178
|
+
to_return(status: 200, body: "", headers: {})
|
112
179
|
|
113
180
|
attributes = { "id" => 5 }
|
114
181
|
|
@@ -119,183 +186,237 @@ describe Customerio::Client do
|
|
119
186
|
describe "#delete" do
|
120
187
|
it "sends a DELETE request to the customer.io's event API" do
|
121
188
|
stub_request(:delete, api_uri('/api/v1/customers/5')).
|
122
|
-
to_return(:
|
189
|
+
to_return(status: 200, body: "", headers: {})
|
123
190
|
|
124
191
|
client.delete(5)
|
125
192
|
end
|
193
|
+
|
194
|
+
it "throws an error when customer_id is missing" do
|
195
|
+
stub_request(:put, /track.customer.io/)
|
196
|
+
.to_return(status: 200, body: "", headers: {})
|
197
|
+
|
198
|
+
lambda { client.delete(" ") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
|
199
|
+
end
|
200
|
+
|
201
|
+
it "escapes customer IDs" do
|
202
|
+
stub_request(:delete, api_uri('/api/v1/customers/5%20')).
|
203
|
+
to_return(status: 200, body: "", headers: {})
|
204
|
+
|
205
|
+
client.delete("5 ")
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
describe "#suppress" do
|
210
|
+
it "sends a POST request to the customer.io's suppress API" do
|
211
|
+
stub_request(:post, api_uri('/api/v1/customers/5/suppress')).
|
212
|
+
to_return(status: 200, body: "", headers: {})
|
213
|
+
|
214
|
+
client.suppress(5)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "throws an error when customer_id is missing" do
|
218
|
+
stub_request(:put, /track.customer.io/)
|
219
|
+
.to_return(status: 200, body: "", headers: {})
|
220
|
+
|
221
|
+
lambda { client.suppress(" ") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "#unsuppress" do
|
226
|
+
it "sends a POST request to the customer.io's unsuppress API" do
|
227
|
+
stub_request(:post, api_uri('/api/v1/customers/5/unsuppress')).
|
228
|
+
to_return(status: 200, body: "", headers: {})
|
229
|
+
|
230
|
+
client.unsuppress(5)
|
231
|
+
end
|
232
|
+
|
233
|
+
it "throws an error when customer_id is missing" do
|
234
|
+
stub_request(:put, /track.customer.io/)
|
235
|
+
.to_return(status: 200, body: "", headers: {})
|
236
|
+
|
237
|
+
lambda { client.suppress(" ") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
|
238
|
+
end
|
126
239
|
end
|
127
240
|
|
128
241
|
describe "#track" do
|
129
242
|
it "raises an error if POST doesn't return a 2xx response code" do
|
130
243
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
131
|
-
with(:
|
132
|
-
to_return(:
|
244
|
+
with(body: json(name: "purchase", data: {})).
|
245
|
+
to_return(status: 500, body: "", headers: {})
|
246
|
+
|
247
|
+
lambda { client.track(5, "purchase") }.should raise_error(Customerio::InvalidResponse)
|
248
|
+
end
|
249
|
+
|
250
|
+
it "throws an error when customer_id or event_name is missing" do
|
251
|
+
stub_request(:put, /track.customer.io/)
|
252
|
+
.to_return(status: 200, body: "", headers: {})
|
133
253
|
|
134
|
-
lambda { client.track(
|
254
|
+
lambda { client.track(" ", "test_event") }.should raise_error(Customerio::Client::ParamError, "customer_id must be a non-empty string")
|
255
|
+
lambda { client.track(5, " ") }.should raise_error(Customerio::Client::ParamError, "event_name must be a non-empty string")
|
135
256
|
end
|
136
257
|
|
137
258
|
it "uses the site_id and api key for basic auth and sends the event name" do
|
138
259
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
139
|
-
with(:
|
140
|
-
to_return(:
|
260
|
+
with(body: json(name: "purchase", data: {})).
|
261
|
+
to_return(status: 200, body: "", headers: {})
|
141
262
|
|
142
263
|
client.track(5, "purchase")
|
143
264
|
end
|
144
265
|
|
145
266
|
it "sends any optional event attributes" do
|
146
267
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
147
|
-
with(:
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
268
|
+
with(body: json({
|
269
|
+
name: "purchase",
|
270
|
+
data: {
|
271
|
+
type: "socks",
|
272
|
+
price: "13.99"
|
152
273
|
}
|
153
|
-
}).
|
154
|
-
to_return(:
|
274
|
+
})).
|
275
|
+
to_return(status: 200, body: "", headers: {})
|
155
276
|
|
156
|
-
client.track(5, "purchase", :
|
277
|
+
client.track(5, "purchase", type: "socks", price: "13.99")
|
157
278
|
end
|
158
279
|
|
159
280
|
it "copes with arrays" do
|
160
281
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
161
|
-
with(:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
282
|
+
with(body: {
|
283
|
+
name: "event",
|
284
|
+
data: {
|
285
|
+
things: ["a", "b", "c"]
|
165
286
|
}
|
166
287
|
}).
|
167
|
-
to_return(:
|
288
|
+
to_return(status: 200, body: "", headers: {})
|
168
289
|
|
169
|
-
client.track(5, "event", :
|
290
|
+
client.track(5, "event", things: ["a", "b", "c"])
|
170
291
|
end
|
171
292
|
|
172
293
|
it "copes with hashes" do
|
173
294
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
174
|
-
with(:
|
175
|
-
:
|
176
|
-
:
|
177
|
-
:
|
295
|
+
with(body: {
|
296
|
+
name: "event",
|
297
|
+
data: {
|
298
|
+
stuff: { a: "b" }
|
178
299
|
}
|
179
300
|
}).
|
180
|
-
to_return(:
|
301
|
+
to_return(status: 200, body: "", headers: {})
|
181
302
|
|
182
|
-
client.track(5, "event", :
|
303
|
+
client.track(5, "event", stuff: { a: "b" })
|
183
304
|
end
|
184
305
|
|
185
306
|
it "sends a POST request as json using json headers" do
|
186
|
-
client = Customerio::Client.new("SITE_ID", "API_KEY", :
|
187
|
-
data = { :
|
188
|
-
body = { :
|
307
|
+
client = Customerio::Client.new("SITE_ID", "API_KEY", json: true)
|
308
|
+
data = { type: "socks", price: "13.99" }
|
309
|
+
body = { name: "purchase", data: data }
|
189
310
|
|
190
311
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
191
|
-
with(:
|
192
|
-
:
|
193
|
-
to_return(:
|
312
|
+
with(body: json(body),
|
313
|
+
headers: {'Content-Type'=>'application/json'}).
|
314
|
+
to_return(status: 200, body: "", headers: {})
|
194
315
|
|
195
316
|
client.track(5, "purchase", data)
|
196
317
|
end
|
197
318
|
|
198
319
|
it "allows sending of a timestamp" do
|
199
320
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
200
|
-
with(:
|
201
|
-
:
|
202
|
-
:
|
203
|
-
:
|
204
|
-
:
|
205
|
-
:
|
321
|
+
with(body: json({
|
322
|
+
name: "purchase",
|
323
|
+
data: {
|
324
|
+
type: "socks",
|
325
|
+
price: "13.99",
|
326
|
+
timestamp: 1561231234
|
206
327
|
},
|
207
|
-
:
|
208
|
-
}).
|
209
|
-
to_return(:
|
328
|
+
timestamp: 1561231234
|
329
|
+
})).
|
330
|
+
to_return(status: 200, body: "", headers: {})
|
210
331
|
|
211
|
-
client.track(5, "purchase", :
|
332
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: 1561231234)
|
212
333
|
end
|
213
334
|
|
214
335
|
it "doesn't send timestamp if timestamp is in milliseconds" do
|
215
336
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
216
|
-
with(:
|
217
|
-
:
|
218
|
-
:
|
219
|
-
:
|
220
|
-
:
|
221
|
-
:
|
337
|
+
with(body: json({
|
338
|
+
name: "purchase",
|
339
|
+
data: {
|
340
|
+
type: "socks",
|
341
|
+
price: "13.99",
|
342
|
+
timestamp: 1561231234000
|
222
343
|
}
|
223
|
-
}).
|
224
|
-
to_return(:
|
344
|
+
})).
|
345
|
+
to_return(status: 200, body: "", headers: {})
|
225
346
|
|
226
|
-
client.track(5, "purchase", :
|
347
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: 1561231234000)
|
227
348
|
end
|
228
349
|
|
229
350
|
it "doesn't send timestamp if timestamp is a date" do
|
230
351
|
date = Time.now
|
231
352
|
|
232
353
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
233
|
-
with(:
|
234
|
-
:
|
235
|
-
:
|
236
|
-
:
|
237
|
-
:
|
238
|
-
:
|
354
|
+
with(body: {
|
355
|
+
name: "purchase",
|
356
|
+
data: {
|
357
|
+
type: "socks",
|
358
|
+
price: "13.99",
|
359
|
+
timestamp: Time.now.to_s
|
239
360
|
}
|
240
361
|
}).
|
241
|
-
to_return(:
|
362
|
+
to_return(status: 200, body: "", headers: {})
|
242
363
|
|
243
|
-
client.track(5, "purchase", :
|
364
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: date)
|
244
365
|
end
|
245
366
|
|
246
367
|
it "doesn't send timestamp if timestamp isn't an integer" do
|
247
368
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
248
|
-
with(:
|
249
|
-
:
|
250
|
-
:
|
251
|
-
:
|
252
|
-
:
|
253
|
-
:
|
369
|
+
with(body: json({
|
370
|
+
name: "purchase",
|
371
|
+
data: {
|
372
|
+
type: "socks",
|
373
|
+
price: "13.99",
|
374
|
+
timestamp: "Hello world"
|
254
375
|
}
|
255
|
-
}).
|
376
|
+
})).
|
256
377
|
|
257
|
-
to_return(:
|
378
|
+
to_return(status: 200, body: "", headers: {})
|
258
379
|
|
259
|
-
client.track(5, "purchase", :
|
380
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: "Hello world")
|
260
381
|
end
|
261
382
|
|
262
383
|
context "tracking an anonymous event" do
|
263
384
|
it "sends a POST request to the customer.io's anonymous event API" do
|
264
385
|
stub_request(:post, api_uri('/api/v1/events')).
|
265
|
-
with(:
|
266
|
-
to_return(:
|
386
|
+
with(body: json({ name: "purchase", data: {} })).
|
387
|
+
to_return(status: 200, body: "", headers: {})
|
267
388
|
|
268
|
-
client.
|
389
|
+
client.anonymous_track("purchase")
|
269
390
|
end
|
270
391
|
|
271
392
|
it "sends any optional event attributes" do
|
272
393
|
stub_request(:post, api_uri('/api/v1/events')).
|
273
|
-
with(:
|
274
|
-
:
|
275
|
-
:
|
276
|
-
:
|
277
|
-
:
|
394
|
+
with(body: json({
|
395
|
+
name: "purchase",
|
396
|
+
data: {
|
397
|
+
type: "socks",
|
398
|
+
price: "13.99"
|
278
399
|
}
|
279
|
-
}).
|
280
|
-
to_return(:
|
400
|
+
})).
|
401
|
+
to_return(status: 200, body: "", headers: {})
|
281
402
|
|
282
|
-
client.
|
403
|
+
client.anonymous_track("purchase", type: "socks", price: "13.99")
|
283
404
|
end
|
284
405
|
|
285
406
|
it "allows sending of a timestamp" do
|
286
407
|
stub_request(:post, api_uri('/api/v1/events')).
|
287
|
-
with(:
|
288
|
-
:
|
289
|
-
:
|
290
|
-
:
|
291
|
-
:
|
292
|
-
:
|
408
|
+
with(body: json({
|
409
|
+
name: "purchase",
|
410
|
+
data: {
|
411
|
+
type: "socks",
|
412
|
+
price: "13.99",
|
413
|
+
timestamp: 1561231234
|
293
414
|
},
|
294
|
-
:
|
295
|
-
}).
|
296
|
-
to_return(:
|
415
|
+
timestamp: 1561231234
|
416
|
+
})).
|
417
|
+
to_return(status: 200, body: "", headers: {})
|
297
418
|
|
298
|
-
client.
|
419
|
+
client.anonymous_track("purchase", type: "socks", price: "13.99", timestamp: 1561231234)
|
299
420
|
end
|
300
421
|
end
|
301
422
|
end
|
@@ -303,55 +424,62 @@ describe Customerio::Client do
|
|
303
424
|
describe "#anonymous_track" do
|
304
425
|
it "raises an error if POST doesn't return a 2xx response code" do
|
305
426
|
stub_request(:post, api_uri('/api/v1/events')).
|
306
|
-
with(:
|
307
|
-
to_return(:
|
427
|
+
with(body: json(name: "purchase", data: {})).
|
428
|
+
to_return(status: 500, body: "", headers: {})
|
308
429
|
|
309
|
-
lambda { client.anonymous_track("purchase") }.should raise_error(Customerio::
|
430
|
+
lambda { client.anonymous_track("purchase") }.should raise_error(Customerio::InvalidResponse)
|
431
|
+
end
|
432
|
+
|
433
|
+
it "throws an error when event_name is missing" do
|
434
|
+
stub_request(:put, /track.customer.io/)
|
435
|
+
.to_return(status: 200, body: "", headers: {})
|
436
|
+
|
437
|
+
lambda { client.anonymous_track(" ") }.should raise_error(Customerio::Client::ParamError, "event_name must be a non-empty string")
|
310
438
|
end
|
311
439
|
|
312
440
|
it "uses the site_id and api key for basic auth and sends the event name" do
|
313
441
|
stub_request(:post, api_uri('/api/v1/events')).
|
314
|
-
with(:
|
315
|
-
to_return(:
|
442
|
+
with(body: json(name: "purchase", data: {})).
|
443
|
+
to_return(status: 200, body: "", headers: {})
|
316
444
|
|
317
445
|
client.anonymous_track("purchase")
|
318
446
|
end
|
319
447
|
|
320
448
|
it "sends any optional event attributes" do
|
321
449
|
stub_request(:post, api_uri('/api/v1/events')).
|
322
|
-
with(:
|
323
|
-
:
|
324
|
-
:
|
325
|
-
:
|
326
|
-
:
|
450
|
+
with(body: {
|
451
|
+
name: "purchase",
|
452
|
+
data: {
|
453
|
+
type: "socks",
|
454
|
+
price: "27.99"
|
327
455
|
},
|
328
456
|
}).
|
329
457
|
|
330
|
-
to_return(:
|
458
|
+
to_return(status: 200, body: "", headers: {})
|
331
459
|
|
332
|
-
client.anonymous_track("purchase", :
|
460
|
+
client.anonymous_track("purchase", type: "socks", price: "27.99")
|
333
461
|
end
|
334
462
|
|
335
463
|
it "allows sending of a timestamp" do
|
336
464
|
stub_request(:post, api_uri('/api/v1/events')).
|
337
|
-
with(:
|
338
|
-
:
|
339
|
-
:
|
340
|
-
:
|
341
|
-
:
|
342
|
-
:
|
465
|
+
with(body: json({
|
466
|
+
name: "purchase",
|
467
|
+
data: {
|
468
|
+
type: "socks",
|
469
|
+
price: "27.99",
|
470
|
+
timestamp: 1561235678
|
343
471
|
},
|
344
|
-
:
|
345
|
-
}).
|
472
|
+
timestamp: 1561235678
|
473
|
+
})).
|
346
474
|
|
347
|
-
to_return(:
|
475
|
+
to_return(status: 200, body: "", headers: {})
|
348
476
|
|
349
|
-
client.anonymous_track("purchase", :
|
477
|
+
client.anonymous_track("purchase", type: "socks", price: "27.99", timestamp: 1561235678)
|
350
478
|
end
|
351
479
|
|
352
480
|
context "too many arguments are passed" do
|
353
481
|
it "throws an error" do
|
354
|
-
lambda { client.anonymous_track("purchase", "text", :
|
482
|
+
lambda { client.anonymous_track("purchase", "text", type: "socks", price: "27.99") }.should raise_error(ArgumentError)
|
355
483
|
end
|
356
484
|
end
|
357
485
|
end
|
@@ -359,60 +487,60 @@ describe Customerio::Client do
|
|
359
487
|
describe "#devices" do
|
360
488
|
it "allows for the creation of a new device" do
|
361
489
|
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
362
|
-
to_return(:
|
490
|
+
to_return(status: 200, body: "", headers: {})
|
363
491
|
|
364
|
-
client.add_device(5, "androidDeviceID", "ios", {:
|
492
|
+
client.add_device(5, "androidDeviceID", "ios", {last_used: 1561235678})
|
365
493
|
client.add_device(5, "iosDeviceID", "android")
|
366
494
|
end
|
367
495
|
it "requires a valid customer_id when creating" do
|
368
496
|
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
369
|
-
to_return(:
|
497
|
+
to_return(status: 200, body: "", headers: {})
|
370
498
|
|
371
499
|
lambda { client.add_device("", "ios", "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
|
372
|
-
lambda { client.add_device(nil, "ios", "myDeviceID", {:
|
500
|
+
lambda { client.add_device(nil, "ios", "myDeviceID", {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
|
373
501
|
end
|
374
502
|
it "requires a valid token when creating" do
|
375
503
|
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
376
|
-
to_return(:
|
504
|
+
to_return(status: 200, body: "", headers: {})
|
377
505
|
|
378
506
|
lambda { client.add_device(5, "", "ios") }.should raise_error(Customerio::Client::ParamError)
|
379
|
-
lambda { client.add_device(5, nil, "ios", {:
|
507
|
+
lambda { client.add_device(5, nil, "ios", {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
|
380
508
|
end
|
381
509
|
it "requires a valid platform when creating" do
|
382
510
|
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
383
|
-
to_return(:
|
511
|
+
to_return(status: 200, body: "", headers: {})
|
384
512
|
|
385
513
|
lambda { client.add_device(5, "token", "") }.should raise_error(Customerio::Client::ParamError)
|
386
|
-
lambda { client.add_device(5, "toke", nil, {:
|
514
|
+
lambda { client.add_device(5, "toke", nil, {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
|
387
515
|
end
|
388
516
|
it "accepts a nil data param" do
|
389
517
|
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
390
|
-
to_return(:
|
518
|
+
to_return(status: 200, body: "", headers: {})
|
391
519
|
|
392
520
|
client.add_device(5, "ios", "myDeviceID", nil)
|
393
521
|
end
|
394
522
|
it "fails on invalid data param" do
|
395
523
|
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
396
|
-
to_return(:
|
524
|
+
to_return(status: 200, body: "", headers: {})
|
397
525
|
|
398
526
|
lambda { client.add_device(5, "ios", "myDeviceID", 1000) }.should raise_error(Customerio::Client::ParamError)
|
399
527
|
end
|
400
528
|
it "supports deletion of devices by token" do
|
401
529
|
stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
|
402
|
-
to_return(:
|
530
|
+
to_return(status: 200, body: "", headers: {})
|
403
531
|
|
404
532
|
client.delete_device(5, "myDeviceID")
|
405
533
|
end
|
406
534
|
it "requires a valid customer_id when deleting" do
|
407
535
|
stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
|
408
|
-
to_return(:
|
536
|
+
to_return(status: 200, body: "", headers: {})
|
409
537
|
|
410
538
|
lambda { client.delete_device("", "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
|
411
539
|
lambda { client.delete_device(nil, "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
|
412
540
|
end
|
413
541
|
it "requires a valid device_id when deleting" do
|
414
542
|
stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
|
415
|
-
to_return(:
|
543
|
+
to_return(status: 200, body: "", headers: {})
|
416
544
|
|
417
545
|
lambda { client.delete_device(5, "") }.should raise_error(Customerio::Client::ParamError)
|
418
546
|
lambda { client.delete_device(5, nil) }.should raise_error(Customerio::Client::ParamError)
|