customerio 3.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
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