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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd807525a01387116c22f29d0ac98e9b783fe669fd092da9d97cac74a660316d
4
- data.tar.gz: 0ff5f3b518f07d5e3432a69e4a5738fd5bdeabc359593960bf9ac8f46081a7fc
3
+ metadata.gz: b4effd60709e999b2530ceb0cdbd5125a163543d5f842657d13bb57b4cf10b92
4
+ data.tar.gz: 6cb74d720acb7f4ac284c760cd92aa4a774d5762dd886ee7d68d8cacc42dd677
5
5
  SHA512:
6
- metadata.gz: 07a1b0e3472abe674a69413e81e6cf2b87c5002a3b074a26053efc0b8da2e7d91d380055c54409922d35ff2d876aeb22c956ce1ea246842ec1ce08ad3079f35b
7
- data.tar.gz: eb291aab81e6e802a204d7c0b2d36890fa2ce26cf82c3af773a8cfddcacdc9b0d4fca7c684aee03a884bafdda4ac088c234294d197fe28041bae2573b186a1da
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 [customer.io](http://customer.io) credentials
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'd like to track events which occurred in the past, you can include a `timestamp` attribute
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 but still want to trigger a campaign:
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
- $customerio.anonymous_track("help_enquiry", :recipient => 'user@example.com')
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.7.0')
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')
@@ -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[:url] = DEFAULT_API_URL if options[:url].nil? || options[:url].empty?
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
 
@@ -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
- DEFAULT_TRACK_URL = 'https://track.customer.io'
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[:url] = DEFAULT_TRACK_URL if options[:url].nil? || options[:url].empty?
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 anonymous_track(event_name, attributes = {})
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
- create_anonymous_event(event_name, attributes)
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("#{customer_path(customer_id)}/events", event_name, attributes)
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("/api/v1/events", event_name, attributes)
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, event_name, attributes = {})
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
@@ -1,3 +1,3 @@
1
1
  module Customerio
2
- VERSION = "3.0.0"
2
+ VERSION = "4.1.0"
3
3
  end
data/lib/customerio.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "customerio/version"
2
2
 
3
3
  module Customerio
4
+ require "customerio/regions"
4
5
  require "customerio/base_client"
5
6
  require "customerio/client"
6
7
  require "customerio/requests/send_email_request"
@@ -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: json({
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
- })).to_return(status: 200, body: "", headers: {})
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: json({ name: "purchase", data: {} })).
390
+ with(body: { anonymous_id: anon_id, name: "purchase", data: {} }).
343
391
  to_return(status: 200, body: "", headers: {})
344
392
 
345
- client.anonymous_track("purchase")
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: json({
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.anonymous_track("purchase", type: "socks", price: "13.99")
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: json({
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.anonymous_track("purchase", type: "socks", price: "13.99", timestamp: 1561231234)
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
- it "uses the site_id and api key for basic auth and sends the event name" do
397
- stub_request(:post, api_uri('/api/v1/events')).
398
- with(body: json(name: "purchase", data: {})).
399
- to_return(status: 200, body: "", headers: {})
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
- client.anonymous_track("purchase")
402
- end
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
- to_return(status: 200, body: "", headers: {})
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
- client.anonymous_track("purchase", type: "socks", price: "27.99")
417
- end
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
- client.anonymous_track("purchase", type: "socks", price: "27.99", timestamp: 1561235678)
434
- end
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
- context "too many arguments are passed" do
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: 3.0.0
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: 2020-12-03 00:00:00.000000000 Z
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.7.0
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.7.0
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
- - ".circleci/config.yml"
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.1.2
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