customerio 1.0.0 → 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.
- checksums.yaml +5 -13
- data/.circleci/config.yml +61 -0
- data/CHANGELOG.markdown +80 -47
- data/README.md +90 -10
- data/customerio.gemspec +5 -4
- data/lib/customerio.rb +3 -0
- data/lib/customerio/api.rb +34 -0
- data/lib/customerio/base_client.rb +87 -0
- data/lib/customerio/client.rb +77 -94
- data/lib/customerio/requests/send_email_request.rb +49 -0
- data/lib/customerio/version.rb +1 -1
- data/spec/api_client_spec.rb +130 -0
- data/spec/base_client_spec.rb +67 -0
- data/spec/client_spec.rb +310 -163
- metadata +44 -37
- data/.travis.yml +0 -14
@@ -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,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(:
|
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 "uses json by default" do
|
26
|
+
body = { id: 5, name: "Bob" }
|
27
|
+
client = Customerio::Client.new("SITE_ID", "API_KEY")
|
19
28
|
|
20
|
-
|
21
|
-
|
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
|
-
|
24
|
-
|
25
|
-
:headers => {'Content-Type'=>'application/json'}).
|
26
|
-
to_return(:status => 200, :body => "", :headers => {})
|
34
|
+
client.identify(body)
|
35
|
+
end
|
27
36
|
|
28
|
-
|
29
|
-
|
37
|
+
describe "headers" do
|
38
|
+
let(:body) { { id: 1, token: :test } }
|
30
39
|
|
31
|
-
it "
|
32
|
-
client = Customerio::Client.new("SITE_ID", "API_KEY"
|
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/
|
35
|
-
with(
|
36
|
-
to_return(:
|
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(:
|
46
|
-
to_return(:
|
54
|
+
with(body: json(id: "5")).
|
55
|
+
to_return(status: 200, body: "", headers: {})
|
56
|
+
|
57
|
+
client.identify(id: "5")
|
58
|
+
end
|
47
59
|
|
48
|
-
|
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 ")
|
66
|
+
|
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", :
|
53
|
-
body = { :
|
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(:
|
57
|
-
:
|
58
|
-
to_return(:
|
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(:
|
66
|
-
to_return(:
|
87
|
+
with(body: json(id: 5)).
|
88
|
+
to_return(status: 500, body: "", headers: {})
|
67
89
|
|
68
|
-
lambda { client.identify(:
|
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(:
|
74
|
-
to_return(:
|
95
|
+
with(body: json(id: 5)).
|
96
|
+
to_return(status: 500, body: "Server unavailable", headers: {})
|
75
97
|
|
76
|
-
lambda { client.identify(:
|
77
|
-
error.should be_a Customerio::
|
78
|
-
error.
|
79
|
-
error.
|
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
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
92
|
-
:
|
93
|
-
}).to_return(:
|
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
|
-
:
|
97
|
-
:
|
98
|
-
:
|
99
|
-
:
|
100
|
-
:
|
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(:
|
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(:
|
111
|
-
to_return(:
|
133
|
+
with(body: json(id: 5)).
|
134
|
+
to_return(status: 200, body: "", headers: {})
|
112
135
|
|
113
136
|
attributes = { "id" => 5 }
|
114
137
|
|
@@ -119,183 +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(:
|
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
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#suppress" do
|
166
|
+
it "sends a POST request to the customer.io's suppress API" do
|
167
|
+
stub_request(:post, api_uri('/api/v1/customers/5/suppress')).
|
168
|
+
to_return(status: 200, body: "", headers: {})
|
169
|
+
|
170
|
+
client.suppress(5)
|
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
|
179
|
+
end
|
180
|
+
|
181
|
+
describe "#unsuppress" do
|
182
|
+
it "sends a POST request to the customer.io's unsuppress API" do
|
183
|
+
stub_request(:post, api_uri('/api/v1/customers/5/unsuppress')).
|
184
|
+
to_return(status: 200, body: "", headers: {})
|
185
|
+
|
186
|
+
client.unsuppress(5)
|
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
|
126
195
|
end
|
127
196
|
|
128
197
|
describe "#track" do
|
129
198
|
it "raises an error if POST doesn't return a 2xx response code" do
|
130
199
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
131
|
-
with(:
|
132
|
-
to_return(:
|
200
|
+
with(body: json(name: "purchase", data: {})).
|
201
|
+
to_return(status: 500, body: "", headers: {})
|
133
202
|
|
134
|
-
lambda { client.track(5, "purchase") }.should raise_error(Customerio::
|
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: {})
|
209
|
+
|
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")
|
135
212
|
end
|
136
213
|
|
137
214
|
it "uses the site_id and api key for basic auth and sends the event name" do
|
138
215
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
139
|
-
with(:
|
140
|
-
to_return(:
|
216
|
+
with(body: json(name: "purchase", data: {})).
|
217
|
+
to_return(status: 200, body: "", headers: {})
|
141
218
|
|
142
219
|
client.track(5, "purchase")
|
143
220
|
end
|
144
221
|
|
145
222
|
it "sends any optional event attributes" do
|
146
223
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
147
|
-
with(:
|
148
|
-
:
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
224
|
+
with(body: json({
|
225
|
+
name: "purchase",
|
226
|
+
data: {
|
227
|
+
type: "socks",
|
228
|
+
price: "13.99"
|
152
229
|
}
|
153
|
-
}).
|
154
|
-
to_return(:
|
230
|
+
})).
|
231
|
+
to_return(status: 200, body: "", headers: {})
|
155
232
|
|
156
|
-
client.track(5, "purchase", :
|
233
|
+
client.track(5, "purchase", type: "socks", price: "13.99")
|
157
234
|
end
|
158
235
|
|
159
236
|
it "copes with arrays" do
|
160
237
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
161
|
-
with(:
|
162
|
-
:
|
163
|
-
:
|
164
|
-
:
|
238
|
+
with(body: {
|
239
|
+
name: "event",
|
240
|
+
data: {
|
241
|
+
things: ["a", "b", "c"]
|
165
242
|
}
|
166
243
|
}).
|
167
|
-
to_return(:
|
244
|
+
to_return(status: 200, body: "", headers: {})
|
168
245
|
|
169
|
-
client.track(5, "event", :
|
246
|
+
client.track(5, "event", things: ["a", "b", "c"])
|
170
247
|
end
|
171
248
|
|
172
249
|
it "copes with hashes" do
|
173
250
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
174
|
-
with(:
|
175
|
-
:
|
176
|
-
:
|
177
|
-
:
|
251
|
+
with(body: {
|
252
|
+
name: "event",
|
253
|
+
data: {
|
254
|
+
stuff: { a: "b" }
|
178
255
|
}
|
179
256
|
}).
|
180
|
-
to_return(:
|
257
|
+
to_return(status: 200, body: "", headers: {})
|
181
258
|
|
182
|
-
client.track(5, "event", :
|
259
|
+
client.track(5, "event", stuff: { a: "b" })
|
183
260
|
end
|
184
261
|
|
185
262
|
it "sends a POST request as json using json headers" do
|
186
|
-
client = Customerio::Client.new("SITE_ID", "API_KEY", :
|
187
|
-
data = { :
|
188
|
-
body = { :
|
263
|
+
client = Customerio::Client.new("SITE_ID", "API_KEY", json: true)
|
264
|
+
data = { type: "socks", price: "13.99" }
|
265
|
+
body = { name: "purchase", data: data }
|
189
266
|
|
190
267
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
191
|
-
with(:
|
192
|
-
:
|
193
|
-
to_return(:
|
268
|
+
with(body: json(body),
|
269
|
+
headers: {'Content-Type'=>'application/json'}).
|
270
|
+
to_return(status: 200, body: "", headers: {})
|
194
271
|
|
195
272
|
client.track(5, "purchase", data)
|
196
273
|
end
|
197
274
|
|
198
275
|
it "allows sending of a timestamp" do
|
199
276
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
200
|
-
with(:
|
201
|
-
:
|
202
|
-
:
|
203
|
-
:
|
204
|
-
:
|
205
|
-
:
|
277
|
+
with(body: json({
|
278
|
+
name: "purchase",
|
279
|
+
data: {
|
280
|
+
type: "socks",
|
281
|
+
price: "13.99",
|
282
|
+
timestamp: 1561231234
|
206
283
|
},
|
207
|
-
:
|
208
|
-
}).
|
209
|
-
to_return(:
|
284
|
+
timestamp: 1561231234
|
285
|
+
})).
|
286
|
+
to_return(status: 200, body: "", headers: {})
|
210
287
|
|
211
|
-
client.track(5, "purchase", :
|
288
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: 1561231234)
|
212
289
|
end
|
213
290
|
|
214
291
|
it "doesn't send timestamp if timestamp is in milliseconds" do
|
215
292
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
216
|
-
with(:
|
217
|
-
:
|
218
|
-
:
|
219
|
-
:
|
220
|
-
:
|
221
|
-
:
|
293
|
+
with(body: json({
|
294
|
+
name: "purchase",
|
295
|
+
data: {
|
296
|
+
type: "socks",
|
297
|
+
price: "13.99",
|
298
|
+
timestamp: 1561231234000
|
222
299
|
}
|
223
|
-
}).
|
224
|
-
to_return(:
|
300
|
+
})).
|
301
|
+
to_return(status: 200, body: "", headers: {})
|
225
302
|
|
226
|
-
client.track(5, "purchase", :
|
303
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: 1561231234000)
|
227
304
|
end
|
228
305
|
|
229
306
|
it "doesn't send timestamp if timestamp is a date" do
|
230
307
|
date = Time.now
|
231
308
|
|
232
309
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
233
|
-
with(:
|
234
|
-
:
|
235
|
-
:
|
236
|
-
:
|
237
|
-
:
|
238
|
-
:
|
310
|
+
with(body: {
|
311
|
+
name: "purchase",
|
312
|
+
data: {
|
313
|
+
type: "socks",
|
314
|
+
price: "13.99",
|
315
|
+
timestamp: Time.now.to_s
|
239
316
|
}
|
240
317
|
}).
|
241
|
-
to_return(:
|
318
|
+
to_return(status: 200, body: "", headers: {})
|
242
319
|
|
243
|
-
client.track(5, "purchase", :
|
320
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: date)
|
244
321
|
end
|
245
322
|
|
246
323
|
it "doesn't send timestamp if timestamp isn't an integer" do
|
247
324
|
stub_request(:post, api_uri('/api/v1/customers/5/events')).
|
248
|
-
with(:
|
249
|
-
:
|
250
|
-
:
|
251
|
-
:
|
252
|
-
:
|
253
|
-
:
|
325
|
+
with(body: json({
|
326
|
+
name: "purchase",
|
327
|
+
data: {
|
328
|
+
type: "socks",
|
329
|
+
price: "13.99",
|
330
|
+
timestamp: "Hello world"
|
254
331
|
}
|
255
|
-
}).
|
332
|
+
})).
|
256
333
|
|
257
|
-
to_return(:
|
334
|
+
to_return(status: 200, body: "", headers: {})
|
258
335
|
|
259
|
-
client.track(5, "purchase", :
|
336
|
+
client.track(5, "purchase", type: "socks", price: "13.99", timestamp: "Hello world")
|
260
337
|
end
|
261
338
|
|
262
339
|
context "tracking an anonymous event" do
|
263
340
|
it "sends a POST request to the customer.io's anonymous event API" do
|
264
341
|
stub_request(:post, api_uri('/api/v1/events')).
|
265
|
-
with(:
|
266
|
-
to_return(:
|
342
|
+
with(body: json({ name: "purchase", data: {} })).
|
343
|
+
to_return(status: 200, body: "", headers: {})
|
267
344
|
|
268
|
-
client.
|
345
|
+
client.anonymous_track("purchase")
|
269
346
|
end
|
270
347
|
|
271
348
|
it "sends any optional event attributes" do
|
272
349
|
stub_request(:post, api_uri('/api/v1/events')).
|
273
|
-
with(:
|
274
|
-
:
|
275
|
-
:
|
276
|
-
:
|
277
|
-
:
|
350
|
+
with(body: json({
|
351
|
+
name: "purchase",
|
352
|
+
data: {
|
353
|
+
type: "socks",
|
354
|
+
price: "13.99"
|
278
355
|
}
|
279
|
-
}).
|
280
|
-
to_return(:
|
356
|
+
})).
|
357
|
+
to_return(status: 200, body: "", headers: {})
|
281
358
|
|
282
|
-
client.
|
359
|
+
client.anonymous_track("purchase", type: "socks", price: "13.99")
|
283
360
|
end
|
284
361
|
|
285
362
|
it "allows sending of a timestamp" do
|
286
363
|
stub_request(:post, api_uri('/api/v1/events')).
|
287
|
-
with(:
|
288
|
-
:
|
289
|
-
:
|
290
|
-
:
|
291
|
-
:
|
292
|
-
:
|
364
|
+
with(body: json({
|
365
|
+
name: "purchase",
|
366
|
+
data: {
|
367
|
+
type: "socks",
|
368
|
+
price: "13.99",
|
369
|
+
timestamp: 1561231234
|
293
370
|
},
|
294
|
-
:
|
295
|
-
}).
|
296
|
-
to_return(:
|
371
|
+
timestamp: 1561231234
|
372
|
+
})).
|
373
|
+
to_return(status: 200, body: "", headers: {})
|
297
374
|
|
298
|
-
client.
|
375
|
+
client.anonymous_track("purchase", type: "socks", price: "13.99", timestamp: 1561231234)
|
299
376
|
end
|
300
377
|
end
|
301
378
|
end
|
@@ -303,56 +380,126 @@ describe Customerio::Client do
|
|
303
380
|
describe "#anonymous_track" do
|
304
381
|
it "raises an error if POST doesn't return a 2xx response code" do
|
305
382
|
stub_request(:post, api_uri('/api/v1/events')).
|
306
|
-
with(:
|
307
|
-
to_return(:
|
383
|
+
with(body: json(name: "purchase", data: {})).
|
384
|
+
to_return(status: 500, body: "", headers: {})
|
385
|
+
|
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: {})
|
308
392
|
|
309
|
-
lambda { client.anonymous_track("
|
393
|
+
lambda { client.anonymous_track(" ") }.should raise_error(Customerio::Client::ParamError, "event_name must be a non-empty string")
|
310
394
|
end
|
311
395
|
|
312
396
|
it "uses the site_id and api key for basic auth and sends the event name" do
|
313
397
|
stub_request(:post, api_uri('/api/v1/events')).
|
314
|
-
with(:
|
315
|
-
to_return(:
|
398
|
+
with(body: json(name: "purchase", data: {})).
|
399
|
+
to_return(status: 200, body: "", headers: {})
|
316
400
|
|
317
401
|
client.anonymous_track("purchase")
|
318
402
|
end
|
319
403
|
|
320
404
|
it "sends any optional event attributes" do
|
321
405
|
stub_request(:post, api_uri('/api/v1/events')).
|
322
|
-
with(:
|
323
|
-
:
|
324
|
-
:
|
325
|
-
:
|
326
|
-
:
|
406
|
+
with(body: {
|
407
|
+
name: "purchase",
|
408
|
+
data: {
|
409
|
+
type: "socks",
|
410
|
+
price: "27.99"
|
327
411
|
},
|
328
412
|
}).
|
329
413
|
|
330
|
-
to_return(:
|
414
|
+
to_return(status: 200, body: "", headers: {})
|
331
415
|
|
332
|
-
client.anonymous_track("purchase", :
|
416
|
+
client.anonymous_track("purchase", type: "socks", price: "27.99")
|
333
417
|
end
|
334
418
|
|
335
419
|
it "allows sending of a timestamp" do
|
336
420
|
stub_request(:post, api_uri('/api/v1/events')).
|
337
|
-
with(:
|
338
|
-
:
|
339
|
-
:
|
340
|
-
:
|
341
|
-
:
|
342
|
-
:
|
421
|
+
with(body: json({
|
422
|
+
name: "purchase",
|
423
|
+
data: {
|
424
|
+
type: "socks",
|
425
|
+
price: "27.99",
|
426
|
+
timestamp: 1561235678
|
343
427
|
},
|
344
|
-
:
|
345
|
-
}).
|
428
|
+
timestamp: 1561235678
|
429
|
+
})).
|
346
430
|
|
347
|
-
to_return(:
|
431
|
+
to_return(status: 200, body: "", headers: {})
|
348
432
|
|
349
|
-
client.anonymous_track("purchase", :
|
433
|
+
client.anonymous_track("purchase", type: "socks", price: "27.99", timestamp: 1561235678)
|
350
434
|
end
|
351
435
|
|
352
436
|
context "too many arguments are passed" do
|
353
437
|
it "throws an error" do
|
354
|
-
lambda { client.anonymous_track("purchase", "text", :
|
438
|
+
lambda { client.anonymous_track("purchase", "text", type: "socks", price: "27.99") }.should raise_error(ArgumentError)
|
355
439
|
end
|
356
440
|
end
|
357
441
|
end
|
442
|
+
|
443
|
+
describe "#devices" do
|
444
|
+
it "allows for the creation of a new device" do
|
445
|
+
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
446
|
+
to_return(status: 200, body: "", headers: {})
|
447
|
+
|
448
|
+
client.add_device(5, "androidDeviceID", "ios", {last_used: 1561235678})
|
449
|
+
client.add_device(5, "iosDeviceID", "android")
|
450
|
+
end
|
451
|
+
it "requires a valid customer_id when creating" do
|
452
|
+
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
453
|
+
to_return(status: 200, body: "", headers: {})
|
454
|
+
|
455
|
+
lambda { client.add_device("", "ios", "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
|
456
|
+
lambda { client.add_device(nil, "ios", "myDeviceID", {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
|
457
|
+
end
|
458
|
+
it "requires a valid token when creating" do
|
459
|
+
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
460
|
+
to_return(status: 200, body: "", headers: {})
|
461
|
+
|
462
|
+
lambda { client.add_device(5, "", "ios") }.should raise_error(Customerio::Client::ParamError)
|
463
|
+
lambda { client.add_device(5, nil, "ios", {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
|
464
|
+
end
|
465
|
+
it "requires a valid platform when creating" do
|
466
|
+
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
467
|
+
to_return(status: 200, body: "", headers: {})
|
468
|
+
|
469
|
+
lambda { client.add_device(5, "token", "") }.should raise_error(Customerio::Client::ParamError)
|
470
|
+
lambda { client.add_device(5, "toke", nil, {last_used: 1561235678}) }.should raise_error(Customerio::Client::ParamError)
|
471
|
+
end
|
472
|
+
it "accepts a nil data param" do
|
473
|
+
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
474
|
+
to_return(status: 200, body: "", headers: {})
|
475
|
+
|
476
|
+
client.add_device(5, "ios", "myDeviceID", nil)
|
477
|
+
end
|
478
|
+
it "fails on invalid data param" do
|
479
|
+
stub_request(:put, api_uri('/api/v1/customers/5/devices')).
|
480
|
+
to_return(status: 200, body: "", headers: {})
|
481
|
+
|
482
|
+
lambda { client.add_device(5, "ios", "myDeviceID", 1000) }.should raise_error(Customerio::Client::ParamError)
|
483
|
+
end
|
484
|
+
it "supports deletion of devices by token" do
|
485
|
+
stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
|
486
|
+
to_return(status: 200, body: "", headers: {})
|
487
|
+
|
488
|
+
client.delete_device(5, "myDeviceID")
|
489
|
+
end
|
490
|
+
it "requires a valid customer_id when deleting" do
|
491
|
+
stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
|
492
|
+
to_return(status: 200, body: "", headers: {})
|
493
|
+
|
494
|
+
lambda { client.delete_device("", "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
|
495
|
+
lambda { client.delete_device(nil, "myDeviceID") }.should raise_error(Customerio::Client::ParamError)
|
496
|
+
end
|
497
|
+
it "requires a valid device_id when deleting" do
|
498
|
+
stub_request(:delete, api_uri('/api/v1/customers/5/devices/myDeviceID')).
|
499
|
+
to_return(status: 200, body: "", headers: {})
|
500
|
+
|
501
|
+
lambda { client.delete_device(5, "") }.should raise_error(Customerio::Client::ParamError)
|
502
|
+
lambda { client.delete_device(5, nil) }.should raise_error(Customerio::Client::ParamError)
|
503
|
+
end
|
504
|
+
end
|
358
505
|
end
|