customerio 3.0.0 → 4.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 +4 -4
- data/.github/workflows/main.yml +30 -0
- data/CHANGELOG.markdown +38 -0
- data/README.md +31 -7
- data/customerio.gemspec +1 -1
- data/lib/customerio/api.rb +4 -3
- data/lib/customerio/client.rb +70 -8
- data/lib/customerio/regions.rb +11 -0
- data/lib/customerio/version.rb +1 -1
- data/lib/customerio.rb +1 -0
- data/spec/api_client_spec.rb +42 -0
- data/spec/client_spec.rb +188 -67
- data/spec/spec_helper.rb +2 -2
- metadata +10 -9
- data/.circleci/config.yml +0 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4effd60709e999b2530ceb0cdbd5125a163543d5f842657d13bb57b4cf10b92
|
4
|
+
data.tar.gz: 6cb74d720acb7f4ac284c760cd92aa4a774d5762dd886ee7d68d8cacc42dd677
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8004b7a1062fabdaa46378a5e9f9a14138ca6bd2c45d6cb246636e112207be11cb68b8a561ca9e507b11ff803ffd8e4f1e0765f6cbecac7dc413c36c9e58adcf
|
7
|
+
data.tar.gz: cf8908e63e075e7a6989c777de2d5bb3f538025bfe6820d6c9d58848fea3ebc3f4a3b054ee94799671f71bf3b1b19678fd0f4051373fcf666c64020eb50b239f
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: ruby_ci
|
2
|
+
|
3
|
+
on: [push,pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
matrix:
|
9
|
+
ruby: ['2.5', '2.6', '2.7']
|
10
|
+
runs-on: ubuntu-latest
|
11
|
+
steps:
|
12
|
+
- uses: actions/checkout@v2
|
13
|
+
- name: set up ruby
|
14
|
+
uses: actions/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
17
|
+
- name: install bundle
|
18
|
+
run: |
|
19
|
+
sudo apt-get -yqq install libpq-dev
|
20
|
+
gem install bundler
|
21
|
+
- name: Cache dependencies
|
22
|
+
uses: actions/cache@v2
|
23
|
+
with:
|
24
|
+
path: vendor/bundle
|
25
|
+
key: customerio-${{ matrix.ruby }}-${{ hashFiles('customerio.gemspec') }}
|
26
|
+
restore-keys: |
|
27
|
+
customerio-${{ matrix.ruby }}-
|
28
|
+
- name: Install dependencies
|
29
|
+
run: bundle install --path vendor/bundle
|
30
|
+
- run: bundle exec rspec
|
data/CHANGELOG.markdown
CHANGED
@@ -1,3 +1,41 @@
|
|
1
|
+
## Customerio 4.1.0 - Sep 27, 2021
|
2
|
+
### Added
|
3
|
+
- Added support for [merge customers](https://customer.io/docs/api/#operation/merge) API
|
4
|
+
|
5
|
+
## Customerio 4.0.1 - July 13, 2021
|
6
|
+
### Changed
|
7
|
+
- Update addressable gem dependency to v2.8.0
|
8
|
+
|
9
|
+
## Customerio 4.0.0 - July 6, 2021
|
10
|
+
### Removed
|
11
|
+
- The `anonymous_track` method.
|
12
|
+
|
13
|
+
### Added
|
14
|
+
- The `track_anonymous` method replaces `anonymous_track`. This method requires an `anonymous_id` parameter and will no longer trigger campaigns. If you previously used anonymous events to trigger campaigns, you can still do so [directly through the API](https://customer.io/docs/api/#operation/trackAnonymous). We now refer to anonymous events that trigger campaigns as ["invite events"](https://customer.io/docs/anonymous-events/#anonymous-or-invite).
|
15
|
+
|
16
|
+
## Customerio 3.1.0 - March 25, 2021
|
17
|
+
### Added
|
18
|
+
- Support for EU region
|
19
|
+
|
20
|
+
### Removed
|
21
|
+
### Changed
|
22
|
+
- `Customerio::Client` and `CustomerIO::APIClient` have a new parameter `region` that can be set to either `Customerio::Regions::EU` or `Customerio::Regions::US` (defaults to `Customerio::Regions::US`)
|
23
|
+
|
24
|
+
## Customerio 3.0.0 - Dec 2, 2020
|
25
|
+
|
26
|
+
### Added
|
27
|
+
- Support for the Transactional API
|
28
|
+
|
29
|
+
### Removed
|
30
|
+
- `add_to_segment` and `remove_from_segment` methods
|
31
|
+
- Support for non-JSON data
|
32
|
+
|
33
|
+
### Changed
|
34
|
+
- IDs in the URLs are now escaped.
|
35
|
+
- Improved validations for data that's passed in.
|
36
|
+
- Earlier, if you passed in an event name without a customer ID to the `track` method, we would create an anonymous event. That is now removed. To create an anonymous event, use the `anonymous_track` method.
|
37
|
+
|
38
|
+
|
1
39
|
## Customerio 3.0.0 - Dec 2, 2020
|
2
40
|
|
3
41
|
### Added
|
data/README.md
CHANGED
@@ -42,15 +42,16 @@ You'll be able to integrate **fully** with [Customer.io](http://customer.io) wit
|
|
42
42
|
|
43
43
|
### Setup
|
44
44
|
|
45
|
-
Create an instance of the client with your [
|
46
|
-
which can be found on the [customer.io integration screen](https://fly.customer.io/account/customerio_integration).
|
45
|
+
Create an instance of the client with your [Customer.io credentials](https://fly.customer.io/settings/api_credentials).
|
47
46
|
|
48
47
|
If you're using Rails, create an initializer `config/initializers/customerio.rb`:
|
49
48
|
|
50
49
|
```ruby
|
51
|
-
$customerio = Customerio::Client.new("YOUR SITE ID", "YOUR API SECRET KEY")
|
50
|
+
$customerio = Customerio::Client.new("YOUR SITE ID", "YOUR API SECRET KEY", region: Customerio::Regions::US)
|
52
51
|
```
|
53
52
|
|
53
|
+
`region` is optional and takes one of two values—`US` or `EU`. If you do not specify your region, we assume that your account is based in the US (`US`). If your account is based in the EU and you do not provide the correct region (`EU`), we'll route requests to our EU data centers accordingly, however this may cause data to be logged in the US.
|
54
|
+
|
54
55
|
### Identify logged in customers
|
55
56
|
|
56
57
|
Tracking data of logged in customers is a key part of [Customer.io](http://customer.io). In order to
|
@@ -103,6 +104,21 @@ recreated.
|
|
103
104
|
$customerio.delete(5)
|
104
105
|
```
|
105
106
|
|
107
|
+
### Merge duplicate customer profiles
|
108
|
+
|
109
|
+
When you merge two people, you pick a primary person and merge a secondary, duplicate person into it. The primary person remains after the merge and the secondary is deleted. This process is permanent: you cannot recover the secondary person.
|
110
|
+
|
111
|
+
The first and third parameters represent the identifier for the primary and secondary people respectively—one of `id`, `email`, or `cio_id`. The second and fourth parameters are the identifier values for the primary and secondary people respectively.
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
# $customerio.merge_customers("primaryType", "primaryIdentifier", "secondaryType", "secondaryIdentifier")
|
115
|
+
# primaryType / secondaryType are one of "id", "email", or "cio_id"
|
116
|
+
# primaryIdentifier / secondaryIdentifier are the identifier value corresponding to the type.
|
117
|
+
|
118
|
+
# merge customer "cperson@gmail.com" into "cool.person@company.com"
|
119
|
+
$customerio.merge_customers("email", "cool.person@company.com", "email", "cperson@gmail.com")
|
120
|
+
```
|
121
|
+
|
106
122
|
### Tracking a custom event
|
107
123
|
|
108
124
|
Now that you're identifying your customers with [Customer.io](http://customer.io), you can now send events like
|
@@ -121,7 +137,7 @@ encourage your customers to perform an action.
|
|
121
137
|
$customerio.track(5, "purchase", :type => "socks", :price => "13.99")
|
122
138
|
```
|
123
139
|
|
124
|
-
**Note:** If you
|
140
|
+
**Note:** If you want to track events which occurred in the past, you can include a `timestamp` attribute
|
125
141
|
(in seconds since the epoch), and we'll use that as the date the event occurred.
|
126
142
|
|
127
143
|
```ruby
|
@@ -130,10 +146,18 @@ $customerio.track(5, "purchase", :type => "socks", :price => "13.99", :timestamp
|
|
130
146
|
|
131
147
|
### Tracking anonymous events
|
132
148
|
|
133
|
-
You can also send anonymous events, for situations where you don't yet have a customer record
|
149
|
+
You can also send anonymous events, for situations where you don't yet have a customer record yet. An anonymous event requires an `anonymous_id` representing the unknown person and an event `name`. When you identify a person, you can set their `anonymous_id` attribute. If [event merging](https://customer.io/docs/anonymous-events/#turn-on-merging) is turned on in your workspace, and the attribute matches the `anonymous_id` in one or more events that were logged within the last 30 days, we associate those events with the person.
|
150
|
+
|
151
|
+
Anonymous events cannot trigger campaigns by themselves. To trigger a campaign, the anonymous event must be associated with a person within 72 hours of the `track_anonymous` request.
|
134
152
|
|
135
153
|
```ruby
|
136
|
-
|
154
|
+
# Arguments
|
155
|
+
# anonymous_id (required) - the id representing the unknown person.
|
156
|
+
# name (required) - the name of the event you want to track.
|
157
|
+
# attributes (optional) - any related information you want to attach to the
|
158
|
+
# event.
|
159
|
+
|
160
|
+
$customerio.track_anonymous(anonymous_id, "product_view", :type => "socks" )
|
137
161
|
```
|
138
162
|
|
139
163
|
Use the `recipient` attribute to specify the email address to send the messages to. [See our documentation on how to use anonymous events for more details](https://learn.customer.io/recipes/invite-emails.html).
|
@@ -195,7 +219,7 @@ Use `send_email` referencing your request to send a transactional message. [Lear
|
|
195
219
|
```ruby
|
196
220
|
require "customerio"
|
197
221
|
|
198
|
-
client = Customerio::APIClient.new("your API key")
|
222
|
+
client = Customerio::APIClient.new("your API key", region: Customerio::Regions::US)
|
199
223
|
|
200
224
|
request = Customerio::SendEmailRequest.new(
|
201
225
|
to: "person@example.com",
|
data/customerio.gemspec
CHANGED
@@ -17,7 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.version = Customerio::VERSION
|
18
18
|
|
19
19
|
gem.add_dependency('multi_json', "~> 1.0")
|
20
|
-
gem.add_dependency('addressable', '~> 2.
|
20
|
+
gem.add_dependency('addressable', '~> 2.8.0')
|
21
21
|
|
22
22
|
gem.add_development_dependency('rake', '~> 10.5')
|
23
23
|
gem.add_development_dependency('rspec', '3.3.0')
|
data/lib/customerio/api.rb
CHANGED
@@ -3,10 +3,11 @@ require 'multi_json'
|
|
3
3
|
|
4
4
|
module Customerio
|
5
5
|
class APIClient
|
6
|
-
DEFAULT_API_URL = 'https://api.customer.io'
|
7
|
-
|
8
6
|
def initialize(app_key, options = {})
|
9
|
-
options[:
|
7
|
+
options[:region] = Customerio::Regions::US if options[:region].nil?
|
8
|
+
raise "region must be an instance of Customerio::Regions::Region" unless options[:region].is_a?(Customerio::Regions::Region)
|
9
|
+
|
10
|
+
options[:url] = options[:region].api_url if options[:url].nil? || options[:url].empty?
|
10
11
|
@client = Customerio::BaseClient.new({ app_key: app_key }, options)
|
11
12
|
end
|
12
13
|
|
data/lib/customerio/client.rb
CHANGED
@@ -1,14 +1,27 @@
|
|
1
1
|
require "addressable/uri"
|
2
2
|
|
3
3
|
module Customerio
|
4
|
+
class IdentifierType
|
5
|
+
ID = "id"
|
6
|
+
EMAIL = "email"
|
7
|
+
CIOID = "cio_id"
|
8
|
+
end
|
9
|
+
|
4
10
|
class Client
|
5
|
-
|
11
|
+
PUSH_OPENED = 'opened'
|
12
|
+
PUSH_CONVERTED = 'converted'
|
13
|
+
PUSH_DELIVERED = 'delivered'
|
14
|
+
|
15
|
+
VALID_PUSH_EVENTS = [PUSH_OPENED, PUSH_CONVERTED, PUSH_DELIVERED]
|
6
16
|
|
7
17
|
class MissingIdAttributeError < RuntimeError; end
|
8
18
|
class ParamError < RuntimeError; end
|
9
19
|
|
10
20
|
def initialize(site_id, api_key, options = {})
|
11
|
-
options[:
|
21
|
+
options[:region] = Customerio::Regions::US if options[:region].nil?
|
22
|
+
raise "region must be an instance of Customerio::Regions::Region" unless options[:region].is_a?(Customerio::Regions::Region)
|
23
|
+
|
24
|
+
options[:url] = options[:region].track_url if options[:url].nil? || options[:url].empty?
|
12
25
|
@client = Customerio::BaseClient.new({ site_id: site_id, api_key: api_key }, options)
|
13
26
|
end
|
14
27
|
|
@@ -38,9 +51,11 @@ module Customerio
|
|
38
51
|
create_customer_event(customer_id, event_name, attributes)
|
39
52
|
end
|
40
53
|
|
41
|
-
def
|
54
|
+
def track_anonymous(anonymous_id, event_name, attributes = {})
|
55
|
+
raise ParamError.new("anonymous_id must be a non-empty string") if is_empty?(anonymous_id)
|
42
56
|
raise ParamError.new("event_name must be a non-empty string") if is_empty?(event_name)
|
43
|
-
|
57
|
+
|
58
|
+
create_anonymous_event(anonymous_id, event_name, attributes)
|
44
59
|
end
|
45
60
|
|
46
61
|
def add_device(customer_id, device_id, platform, data={})
|
@@ -69,6 +84,30 @@ module Customerio
|
|
69
84
|
@client.request_and_verify_response(:delete, device_id_path(customer_id, device_id))
|
70
85
|
end
|
71
86
|
|
87
|
+
def track_push_notification_event(event_name, attributes = {})
|
88
|
+
keys = [:delivery_id, :device_id, :timestamp]
|
89
|
+
attributes = Hash[attributes.map { |(k,v)| [ k.to_sym, v ] }].
|
90
|
+
select { |k, v| keys.include?(k) }
|
91
|
+
|
92
|
+
raise ParamError.new('event_name must be one of opened, converted, or delivered') unless VALID_PUSH_EVENTS.include?(event_name)
|
93
|
+
raise ParamError.new('delivery_id must be a non-empty string') unless attributes[:delivery_id] != "" and !attributes[:delivery_id].nil?
|
94
|
+
raise ParamError.new('device_id must be a non-empty string') unless attributes[:device_id] != "" and !attributes[:device_id].nil?
|
95
|
+
raise ParamError.new('timestamp must be a valid timestamp') unless valid_timestamp?(attributes[:timestamp])
|
96
|
+
|
97
|
+
@client.request_and_verify_response(:post, track_push_notification_event_path, attributes.merge(event: event_name))
|
98
|
+
end
|
99
|
+
|
100
|
+
def merge_customers(primary_id_type, primary_id, secondary_id_type, secondary_id)
|
101
|
+
raise ParamError.new("invalid primary_id_type") if !is_valid_id_type?(primary_id_type)
|
102
|
+
raise ParamError.new("primary_id must be a non-empty string") if is_empty?(primary_id)
|
103
|
+
raise ParamError.new("invalid secondary_id_type") if !is_valid_id_type?(secondary_id_type)
|
104
|
+
raise ParamError.new("secondary_id must be a non-empty string") if is_empty?(secondary_id)
|
105
|
+
|
106
|
+
body = { :primary => {primary_id_type => primary_id}, :secondary => {secondary_id_type => secondary_id} }
|
107
|
+
|
108
|
+
@client.request_and_verify_response(:post, merge_customers_path, body)
|
109
|
+
end
|
110
|
+
|
72
111
|
private
|
73
112
|
|
74
113
|
def escape(val)
|
@@ -96,6 +135,14 @@ module Customerio
|
|
96
135
|
"/api/v1/customers/#{escape(customer_id)}/unsuppress"
|
97
136
|
end
|
98
137
|
|
138
|
+
def track_push_notification_event_path
|
139
|
+
"/push/events"
|
140
|
+
end
|
141
|
+
|
142
|
+
def merge_customers_path
|
143
|
+
"/api/v1/merge_customers"
|
144
|
+
end
|
145
|
+
|
99
146
|
def create_or_update(attributes = {})
|
100
147
|
attributes = Hash[attributes.map { |(k,v)| [ k.to_sym, v ] }]
|
101
148
|
raise MissingIdAttributeError.new("Must provide a customer id") if is_empty?(attributes[:id])
|
@@ -105,16 +152,27 @@ module Customerio
|
|
105
152
|
end
|
106
153
|
|
107
154
|
def create_customer_event(customer_id, event_name, attributes = {})
|
108
|
-
create_event(
|
155
|
+
create_event(
|
156
|
+
url: "#{customer_path(customer_id)}/events",
|
157
|
+
event_name: event_name,
|
158
|
+
attributes: attributes
|
159
|
+
)
|
109
160
|
end
|
110
161
|
|
111
|
-
def create_anonymous_event(event_name, attributes = {})
|
112
|
-
create_event(
|
162
|
+
def create_anonymous_event(anonymous_id, event_name, attributes = {})
|
163
|
+
create_event(
|
164
|
+
url: "/api/v1/events",
|
165
|
+
event_name: event_name,
|
166
|
+
anonymous_id: anonymous_id,
|
167
|
+
attributes: attributes
|
168
|
+
)
|
113
169
|
end
|
114
170
|
|
115
|
-
def create_event(url
|
171
|
+
def create_event(url:, event_name:, anonymous_id: nil, attributes: {})
|
116
172
|
body = { :name => event_name, :data => attributes }
|
117
173
|
body[:timestamp] = attributes[:timestamp] if valid_timestamp?(attributes[:timestamp])
|
174
|
+
body[:anonymous_id] = anonymous_id unless anonymous_id.nil?
|
175
|
+
|
118
176
|
@client.request_and_verify_response(:post, url, body)
|
119
177
|
end
|
120
178
|
|
@@ -125,5 +183,9 @@ module Customerio
|
|
125
183
|
def is_empty?(val)
|
126
184
|
val.nil? || (val.is_a?(String) && val.strip == "")
|
127
185
|
end
|
186
|
+
|
187
|
+
def is_valid_id_type?(input)
|
188
|
+
[IdentifierType::ID, IdentifierType::CIOID, IdentifierType::EMAIL].include? input
|
189
|
+
end
|
128
190
|
end
|
129
191
|
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Customerio
|
5
|
+
module Regions
|
6
|
+
Region = Struct.new(:track_url, :api_url)
|
7
|
+
|
8
|
+
US = Customerio::Regions::Region.new('https://track.customer.io', 'https://api.customer.io').freeze
|
9
|
+
EU = Customerio::Regions::Region.new('https://track-eu.customer.io', 'https://api-eu.customer.io').freeze
|
10
|
+
end
|
11
|
+
end
|
data/lib/customerio/version.rb
CHANGED
data/lib/customerio.rb
CHANGED
data/spec/api_client_spec.rb
CHANGED
@@ -21,6 +21,48 @@ describe Customerio::APIClient do
|
|
21
21
|
MultiJson.dump(data)
|
22
22
|
end
|
23
23
|
|
24
|
+
it "the base client is initialised with the correct values when no region is passed in" do
|
25
|
+
app_key = "appkey"
|
26
|
+
|
27
|
+
expect(Customerio::BaseClient).to(
|
28
|
+
receive(:new)
|
29
|
+
.with(
|
30
|
+
{ app_key: app_key },
|
31
|
+
{
|
32
|
+
region: Customerio::Regions::US,
|
33
|
+
url: Customerio::Regions::US.api_url
|
34
|
+
}
|
35
|
+
)
|
36
|
+
)
|
37
|
+
|
38
|
+
client = Customerio::APIClient.new(app_key)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "raises an error when an incorrect region is passed in" do
|
42
|
+
expect {
|
43
|
+
Customerio::APIClient.new("appkey", region: :au)
|
44
|
+
}.to raise_error /region must be an instance of Customerio::Regions::Region/
|
45
|
+
end
|
46
|
+
|
47
|
+
[Customerio::Regions::US, Customerio::Regions::EU].each do |region|
|
48
|
+
it "the base client is initialised with the correct values when the region \"#{region}\" is passed in" do
|
49
|
+
app_key = "appkey"
|
50
|
+
|
51
|
+
expect(Customerio::BaseClient).to(
|
52
|
+
receive(:new)
|
53
|
+
.with(
|
54
|
+
{ app_key: app_key },
|
55
|
+
{
|
56
|
+
region: region,
|
57
|
+
url: region.api_url
|
58
|
+
}
|
59
|
+
)
|
60
|
+
)
|
61
|
+
|
62
|
+
client = Customerio::APIClient.new(app_key, { region: region })
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
24
66
|
describe "#send_email" do
|
25
67
|
it "sends a POST request to the /api/send/email path" do
|
26
68
|
req = Customerio::SendEmailRequest.new(
|
data/spec/client_spec.rb
CHANGED
@@ -22,6 +22,50 @@ describe Customerio::Client do
|
|
22
22
|
MultiJson.dump(data)
|
23
23
|
end
|
24
24
|
|
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
|
+
)
|
39
|
+
|
40
|
+
client = Customerio::Client.new(site_id, api_key)
|
41
|
+
end
|
42
|
+
|
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
|
48
|
+
|
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 })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
25
69
|
it "uses json by default" do
|
26
70
|
body = { id: 5, name: "Bob" }
|
27
71
|
client = Customerio::Client.new("SITE_ID", "API_KEY")
|
@@ -106,16 +150,18 @@ describe Customerio::Client do
|
|
106
150
|
time = Time.now.to_i
|
107
151
|
|
108
152
|
stub_request(:put, api_uri('/api/v1/customers/5')).with(
|
109
|
-
body:
|
153
|
+
body: {
|
110
154
|
id: 5,
|
111
155
|
email: "customer@example.com",
|
112
156
|
created_at: time,
|
113
157
|
first_name: "Bob",
|
114
|
-
plan: "basic"
|
115
|
-
|
158
|
+
plan: "basic",
|
159
|
+
anonymous_id: "anon-id"
|
160
|
+
}).to_return(status: 200, body: "", headers: {})
|
116
161
|
|
117
162
|
client.identify({
|
118
163
|
id: 5,
|
164
|
+
anonymous_id: "anon-id",
|
119
165
|
email: "customer@example.com",
|
120
166
|
created_at: time,
|
121
167
|
first_name: "Bob",
|
@@ -337,31 +383,35 @@ describe Customerio::Client do
|
|
337
383
|
end
|
338
384
|
|
339
385
|
context "tracking an anonymous event" do
|
386
|
+
let(:anon_id) { "anon-id" }
|
387
|
+
|
340
388
|
it "sends a POST request to the customer.io's anonymous event API" do
|
341
389
|
stub_request(:post, api_uri('/api/v1/events')).
|
342
|
-
with(body:
|
390
|
+
with(body: { anonymous_id: anon_id, name: "purchase", data: {} }).
|
343
391
|
to_return(status: 200, body: "", headers: {})
|
344
392
|
|
345
|
-
client.
|
393
|
+
client.track_anonymous(anon_id, "purchase")
|
346
394
|
end
|
347
395
|
|
348
396
|
it "sends any optional event attributes" do
|
349
397
|
stub_request(:post, api_uri('/api/v1/events')).
|
350
|
-
with(body:
|
398
|
+
with(body: {
|
399
|
+
anonymous_id: anon_id,
|
351
400
|
name: "purchase",
|
352
401
|
data: {
|
353
402
|
type: "socks",
|
354
403
|
price: "13.99"
|
355
404
|
}
|
356
|
-
})
|
405
|
+
}).
|
357
406
|
to_return(status: 200, body: "", headers: {})
|
358
407
|
|
359
|
-
client.
|
408
|
+
client.track_anonymous(anon_id, "purchase", type: "socks", price: "13.99")
|
360
409
|
end
|
361
410
|
|
362
411
|
it "allows sending of a timestamp" do
|
363
412
|
stub_request(:post, api_uri('/api/v1/events')).
|
364
|
-
with(body:
|
413
|
+
with(body: {
|
414
|
+
anonymous_id: anon_id,
|
365
415
|
name: "purchase",
|
366
416
|
data: {
|
367
417
|
type: "socks",
|
@@ -369,73 +419,34 @@ describe Customerio::Client do
|
|
369
419
|
timestamp: 1561231234
|
370
420
|
},
|
371
421
|
timestamp: 1561231234
|
372
|
-
})
|
422
|
+
}).
|
373
423
|
to_return(status: 200, body: "", headers: {})
|
374
424
|
|
375
|
-
client.
|
425
|
+
client.track_anonymous(anon_id, "purchase", type: "socks", price: "13.99", timestamp: 1561231234)
|
376
426
|
end
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
describe "#anonymous_track" do
|
381
|
-
it "raises an error if POST doesn't return a 2xx response code" do
|
382
|
-
stub_request(:post, api_uri('/api/v1/events')).
|
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: {})
|
392
|
-
|
393
|
-
lambda { client.anonymous_track(" ") }.should raise_error(Customerio::Client::ParamError, "event_name must be a non-empty string")
|
394
|
-
end
|
395
427
|
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
428
|
+
it "raises an error if POST doesn't return a 2xx response code" do
|
429
|
+
stub_request(:post, api_uri('/api/v1/events')).
|
430
|
+
with(body: { anonymous_id: anon_id, name: "purchase", data: {} }).
|
431
|
+
to_return(status: 500, body: "", headers: {})
|
400
432
|
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
it "sends any optional event attributes" do
|
405
|
-
stub_request(:post, api_uri('/api/v1/events')).
|
406
|
-
with(body: {
|
407
|
-
name: "purchase",
|
408
|
-
data: {
|
409
|
-
type: "socks",
|
410
|
-
price: "27.99"
|
411
|
-
},
|
412
|
-
}).
|
433
|
+
lambda { client.track_anonymous(anon_id, "purchase") }.should raise_error(Customerio::InvalidResponse)
|
434
|
+
end
|
413
435
|
|
414
|
-
|
436
|
+
it "throws an error when anonymous_id is missing" do
|
437
|
+
stub_request(:post, api_uri('/api/v1/events')).
|
438
|
+
with(body: { anonymous_id: anon_id, name: "purchase", data: {} }).
|
439
|
+
to_return(status: 500, body: "", headers: {})
|
415
440
|
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
it "allows sending of a timestamp" do
|
420
|
-
stub_request(:post, api_uri('/api/v1/events')).
|
421
|
-
with(body: json({
|
422
|
-
name: "purchase",
|
423
|
-
data: {
|
424
|
-
type: "socks",
|
425
|
-
price: "27.99",
|
426
|
-
timestamp: 1561235678
|
427
|
-
},
|
428
|
-
timestamp: 1561235678
|
429
|
-
})).
|
430
|
-
|
431
|
-
to_return(status: 200, body: "", headers: {})
|
441
|
+
lambda { client.track_anonymous("", "some_event") }.should raise_error(Customerio::Client::ParamError)
|
442
|
+
end
|
432
443
|
|
433
|
-
|
434
|
-
|
444
|
+
it "throws an error when event_name is missing" do
|
445
|
+
stub_request(:post, api_uri('/api/v1/events')).
|
446
|
+
with(body: { anonymous_id: anon_id, name: "purchase", data: {} }).
|
447
|
+
to_return(status: 500, body: "", headers: {})
|
435
448
|
|
436
|
-
|
437
|
-
it "throws an error" do
|
438
|
-
lambda { client.anonymous_track("purchase", "text", type: "socks", price: "27.99") }.should raise_error(ArgumentError)
|
449
|
+
lambda { client.track_anonymous(anon_id, "") }.should raise_error(Customerio::Client::ParamError)
|
439
450
|
end
|
440
451
|
end
|
441
452
|
end
|
@@ -502,4 +513,114 @@ describe Customerio::Client do
|
|
502
513
|
lambda { client.delete_device(5, nil) }.should raise_error(Customerio::Client::ParamError)
|
503
514
|
end
|
504
515
|
end
|
516
|
+
|
517
|
+
describe "#track_push_notification_event" do
|
518
|
+
attr_accessor :client, :attributes
|
519
|
+
|
520
|
+
before(:each) do
|
521
|
+
@client = Customerio::Client.new("SITE_ID", "API_KEY", :json => true)
|
522
|
+
@attributes = {
|
523
|
+
:delivery_id => 'foo',
|
524
|
+
:device_id => 'bar',
|
525
|
+
:timestamp => Time.now.to_i
|
526
|
+
}
|
527
|
+
end
|
528
|
+
|
529
|
+
it "sends a POST request to customer.io's /push/events endpoint" do
|
530
|
+
stub_request(:post, api_uri('/push/events')).
|
531
|
+
with(
|
532
|
+
:body => json(attributes.merge({
|
533
|
+
:event => 'opened'
|
534
|
+
})),
|
535
|
+
:headers => {
|
536
|
+
'Content-Type' => 'application/json'
|
537
|
+
}).
|
538
|
+
to_return(:status => 200, :body => "", :headers => {})
|
539
|
+
|
540
|
+
client.track_push_notification_event('opened', attributes)
|
541
|
+
end
|
542
|
+
|
543
|
+
it "should raise if event is invalid" do
|
544
|
+
stub_request(:post, api_uri('/push/events')).
|
545
|
+
to_return(:status => 200, :body => "", :headers => {})
|
546
|
+
|
547
|
+
expect {
|
548
|
+
client.track_push_notification_event('closed', attributes.merge({ :delivery_id => nil }))
|
549
|
+
}.to raise_error(Customerio::Client::ParamError, 'event_name must be one of opened, converted, or delivered')
|
550
|
+
end
|
551
|
+
|
552
|
+
it "should raise if delivery_id is invalid" do
|
553
|
+
stub_request(:post, api_uri('/push/events')).
|
554
|
+
to_return(:status => 200, :body => "", :headers => {})
|
555
|
+
|
556
|
+
expect {
|
557
|
+
client.track_push_notification_event('opened', attributes.merge({ :delivery_id => nil }))
|
558
|
+
}.to raise_error(Customerio::Client::ParamError, 'delivery_id must be a non-empty string')
|
559
|
+
|
560
|
+
expect {
|
561
|
+
client.track_push_notification_event('opened', attributes.merge({ :delivery_id => '' }))
|
562
|
+
}.to raise_error(Customerio::Client::ParamError, 'delivery_id must be a non-empty string')
|
563
|
+
end
|
564
|
+
|
565
|
+
it "should raise if device_id is invalid" do
|
566
|
+
stub_request(:post, api_uri('/push/events')).
|
567
|
+
to_return(:status => 200, :body => "", :headers => {})
|
568
|
+
|
569
|
+
expect {
|
570
|
+
client.track_push_notification_event('opened', attributes.merge({ :device_id => nil }))
|
571
|
+
}.to raise_error(Customerio::Client::ParamError, 'device_id must be a non-empty string')
|
572
|
+
|
573
|
+
expect {
|
574
|
+
client.track_push_notification_event('opened', attributes.merge({ :device_id => '' }))
|
575
|
+
}.to raise_error(Customerio::Client::ParamError, 'device_id must be a non-empty string')
|
576
|
+
end
|
577
|
+
|
578
|
+
it "should raise if timestamp is invalid" do
|
579
|
+
stub_request(:post, api_uri('/push/events')).
|
580
|
+
to_return(:status => 200, :body => "", :headers => {})
|
581
|
+
|
582
|
+
expect {
|
583
|
+
client.track_push_notification_event('opened', attributes.merge({ :timestamp => nil }))
|
584
|
+
}.to raise_error(Customerio::Client::ParamError, 'timestamp must be a valid timestamp')
|
585
|
+
|
586
|
+
expect {
|
587
|
+
client.track_push_notification_event('opened', attributes.merge({ :timestamp => 999999999 }))
|
588
|
+
}.to raise_error(Customerio::Client::ParamError, 'timestamp must be a valid timestamp')
|
589
|
+
|
590
|
+
expect {
|
591
|
+
client.track_push_notification_event('opened', attributes.merge({ :timestamp => 100000000000 }))
|
592
|
+
}.to raise_error(Customerio::Client::ParamError, 'timestamp must be a valid timestamp')
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
describe "#merge_customers" do
|
597
|
+
before(:each) do
|
598
|
+
@client = Customerio::Client.new("SITE_ID", "API_KEY", :json => true)
|
599
|
+
end
|
600
|
+
|
601
|
+
it "should raise validation errors on merge params" do
|
602
|
+
expect {
|
603
|
+
client.merge_customers("", "id1", Customerio::IdentifierType::ID, "id2")
|
604
|
+
}.to raise_error(Customerio::Client::ParamError, 'invalid primary_id_type')
|
605
|
+
|
606
|
+
expect {
|
607
|
+
client.merge_customers(Customerio::IdentifierType::EMAIL, "", Customerio::IdentifierType::ID, "id2")
|
608
|
+
}.to raise_error(Customerio::Client::ParamError, 'primary_id must be a non-empty string')
|
609
|
+
|
610
|
+
expect {
|
611
|
+
client.merge_customers(Customerio::IdentifierType::CIOID, "id1", "", "id2")
|
612
|
+
}.to raise_error(Customerio::Client::ParamError, 'invalid secondary_id_type')
|
613
|
+
|
614
|
+
expect {
|
615
|
+
client.merge_customers(Customerio::IdentifierType::ID, "id1", Customerio::IdentifierType::ID, "")
|
616
|
+
}.to raise_error(Customerio::Client::ParamError, 'secondary_id must be a non-empty string')
|
617
|
+
end
|
618
|
+
|
619
|
+
it "requires a valid customer_id when creating" do
|
620
|
+
stub_request(:post, api_uri('/api/v1/merge_customers')).
|
621
|
+
to_return(status: 200, body: "", headers: {})
|
622
|
+
|
623
|
+
client.merge_customers(Customerio::IdentifierType::ID, "ID1", Customerio::IdentifierType::EMAIL, "hello@company.com")
|
624
|
+
end
|
625
|
+
end
|
505
626
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,6 @@ require 'rspec'
|
|
10
10
|
require 'webmock/rspec'
|
11
11
|
|
12
12
|
RSpec.configure do |config|
|
13
|
-
config.expect_with(:rspec) { |c| c.syntax = :should }
|
14
|
-
config.mock_with(:rspec) { |c| c.syntax = :should }
|
13
|
+
config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
|
14
|
+
config.mock_with(:rspec) { |c| c.syntax = [:should, :expect] }
|
15
15
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: customerio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Allison
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: multi_json
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 2.
|
33
|
+
version: 2.8.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 2.
|
40
|
+
version: 2.8.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -101,7 +101,7 @@ executables: []
|
|
101
101
|
extensions: []
|
102
102
|
extra_rdoc_files: []
|
103
103
|
files:
|
104
|
-
- ".
|
104
|
+
- ".github/workflows/main.yml"
|
105
105
|
- ".gitignore"
|
106
106
|
- CHANGELOG.markdown
|
107
107
|
- Gemfile
|
@@ -114,6 +114,7 @@ files:
|
|
114
114
|
- lib/customerio/base_client.rb
|
115
115
|
- lib/customerio/client.rb
|
116
116
|
- lib/customerio/param_encoder.rb
|
117
|
+
- lib/customerio/regions.rb
|
117
118
|
- lib/customerio/requests/send_email_request.rb
|
118
119
|
- lib/customerio/version.rb
|
119
120
|
- spec/api_client_spec.rb
|
@@ -124,7 +125,7 @@ homepage: http://customer.io
|
|
124
125
|
licenses:
|
125
126
|
- MIT
|
126
127
|
metadata: {}
|
127
|
-
post_install_message:
|
128
|
+
post_install_message:
|
128
129
|
rdoc_options: []
|
129
130
|
require_paths:
|
130
131
|
- lib
|
@@ -139,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
140
|
- !ruby/object:Gem::Version
|
140
141
|
version: '0'
|
141
142
|
requirements: []
|
142
|
-
rubygems_version: 3.
|
143
|
-
signing_key:
|
143
|
+
rubygems_version: 3.2.22
|
144
|
+
signing_key:
|
144
145
|
specification_version: 4
|
145
146
|
summary: A ruby client for the Customer.io event API.
|
146
147
|
test_files:
|
data/.circleci/config.yml
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
---
|
2
|
-
version: 2
|
3
|
-
|
4
|
-
references:
|
5
|
-
restore_cache: &restore_cache
|
6
|
-
type: cache-restore
|
7
|
-
name: Restore bundle cache
|
8
|
-
key: customerio-ruby-{{ checksum "customerio.gemspec" }}
|
9
|
-
|
10
|
-
save_cache: &save_cache
|
11
|
-
type: cache-save
|
12
|
-
name: Store bundle cache
|
13
|
-
key: customerio-ruby-{{ checksum "customerio.gemspec" }}
|
14
|
-
paths:
|
15
|
-
- vendor/bundle
|
16
|
-
|
17
|
-
jobs:
|
18
|
-
ruby_27:
|
19
|
-
working_directory: ~/customerio-ruby
|
20
|
-
docker:
|
21
|
-
- image: circleci/ruby:2.7.2
|
22
|
-
environment:
|
23
|
-
RAILS_ENV: test
|
24
|
-
steps:
|
25
|
-
- checkout
|
26
|
-
- *restore_cache
|
27
|
-
- run: bundle install
|
28
|
-
- *save_cache
|
29
|
-
- run: bundle exec rspec
|
30
|
-
ruby_26:
|
31
|
-
working_directory: ~/customerio-ruby
|
32
|
-
docker:
|
33
|
-
- image: circleci/ruby:2.6.6
|
34
|
-
environment:
|
35
|
-
RAILS_ENV: test
|
36
|
-
steps:
|
37
|
-
- checkout
|
38
|
-
- *restore_cache
|
39
|
-
- run: bundle install
|
40
|
-
- *save_cache
|
41
|
-
- run: bundle exec rspec
|
42
|
-
ruby_25:
|
43
|
-
working_directory: ~/customerio-ruby
|
44
|
-
docker:
|
45
|
-
- image: circleci/ruby:2.5.8
|
46
|
-
environment:
|
47
|
-
RAILS_ENV: test
|
48
|
-
steps:
|
49
|
-
- checkout
|
50
|
-
- *restore_cache
|
51
|
-
- run: bundle install
|
52
|
-
- *save_cache
|
53
|
-
- run: bundle exec rspec
|
54
|
-
|
55
|
-
workflows:
|
56
|
-
version: 2
|
57
|
-
test:
|
58
|
-
jobs:
|
59
|
-
- ruby_27
|
60
|
-
- ruby_26
|
61
|
-
- ruby_25
|