Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,56 @@ rescue Customerio::InvalidResponse => e
end
```

### Trigger Broadcasts

To use the Customer.io [Trigger Broadcast API](https://www.customer.io/docs/api/app/#operation/triggerBroadcast), create an instance of the API client using an [app key](https://customer.io/docs/managing-credentials#app-api-keys).

Create a new `TriggerBroadcastRequest` object containing:

* `broadcast_id`: the ID of the broadcast you want to trigger.

Optionally, add a `payload` object with any of the following:
* one of the audience options to override the default audience: `recipients | emails | ids | per_user_data | data_file_url`.
* `data`: an object containing information you want to use to populate your broadcast.
* `email_add_duplicates`: a boolean indicating if an email address associated with more than one profile id is an error (default: false).
* `email_ignore_missing`: if false, a missing email address is an error (default: false).
* `id_ignore_missing`: if false, a missing customer ID is an error (default: false).

Use `trigger_broadcast` referencing your request to trigger the broadcast. [Learn more about triggering broadcasts and `TriggerBroadcastRequest` properties](https://customer.io/docs/api-triggered-broadcasts/).

```ruby
require "customerio"

client = Customerio::APIClient.new("your API key", region: Customerio::Regions::US)

payload = {
emails: [
"recipient1@example.com",
"anotherRecipient@example.com"
],
data: {
headline: "Roadrunner spotted in Albuquerque!",
date: 1511315635,
text: "We received reports of a roadrunner in your immediate area! Head to your dashboard to view more information!"
},
email_add_duplicates: false,
email_ignore_missing: false,
id_ignore_missing: false
}

request = Customerio::TriggerBroadcastRequest.new(
broadcast_id: 12345,
payload: payload
)

begin
response = client.trigger_broadcast(request)
puts response
rescue Customerio::InvalidResponse => e
puts e.code, e.message
end
```

## Contributing

1. Fork it
Expand Down
1 change: 1 addition & 0 deletions lib/customerio.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Customerio
require "customerio/client"
require "customerio/requests/send_email_request"
require "customerio/requests/send_push_request"
require "customerio/requests/trigger_broadcast_request"
require "customerio/api"
require "customerio/param_encoder"
end
22 changes: 22 additions & 0 deletions lib/customerio/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,24 @@ def send_push(req)
end
end

def trigger_broadcast(req)
unless req.is_a?(Customerio::TriggerBroadcastRequest)
raise 'request must be an instance of Customerio::TriggerBroadcastRequest'
end

response = @client.request(:post, trigger_broadcast_path(req.broadcast_id), req.payload)

case response
when Net::HTTPSuccess
JSON.parse(response.body)
when Net::HTTPBadRequest
json = JSON.parse(response.body)
raise Customerio::InvalidResponse.new(response.code, json['meta']['error'], response)
else
raise InvalidResponse.new(response.code, response.body)
end
end

private

def send_email_path
Expand All @@ -50,5 +68,9 @@ def send_email_path
def send_push_path
"/v1/send/push"
end

def trigger_broadcast_path(broadcast_id)
"/v1/campaigns/#{broadcast_id}/triggers"
end
end
end
41 changes: 41 additions & 0 deletions lib/customerio/requests/trigger_broadcast_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module Customerio
class TriggerBroadcastRequest
attr_reader :broadcast_id, :payload

def initialize(broadcast_id:, payload:{})
@broadcast_id = broadcast_id
@payload = payload.delete_if { |field| invalid_field?(field) }

validate_broadcast_id
validate_xor_recipients
end

private

OPTIONAL_FIELDS = [:data, :email_add_duplicates, :email_ignore_missing, :id_ignore_missing].freeze

# we're not validating the structure, just that only one is present
ONLY_ONE_ALLOWED = [
:recipients,
:emails,
:ids,
:per_user_data,
:data_file_url
].freeze

def invalid_field?(field)
!OPTIONAL_FIELDS.include?(field) && !ONLY_ONE_ALLOWED.include?(field)
end

def validate_broadcast_id
raise 'broadcast id is required' unless broadcast_id
raise 'broadcast id must be an integer' unless broadcast_id.is_a?(Integer)
end

def validate_xor_recipients
present = ONLY_ONE_ALLOWED.select { |field| payload.key?(field) }

raise "Only one of #{arr.join(', ')} can be present" if present.length > 1
end
end
end
109 changes: 109 additions & 0 deletions spec/api_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,113 @@ def json(data)
client.send_push(req).should eq({ "delivery_id" => 2 })
end
end

describe '#trigger_broadcast' do
it "sends a POST request to the customer.io's broadcast API" do
payload = {
data: { name: 'foo' },
recipients: {
segment: { id: 7 }
}
}
req = Customerio::TriggerBroadcastRequest.new(broadcast_id: 1, payload: payload)

stub_request(:post, api_uri('/v1/campaigns/1/triggers'))
.with(headers: request_headers, body: req.payload)
.to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})

expect(client.trigger_broadcast(req)).to eq({ 'delivery_id' => 1 })
end

it "handles validation failures (400)" do
payload = {
data: { name: 'foo' },
emails: ['foo', 'bar'],
email_ignore_missing: true,
email_add_duplicates: true
}
req = Customerio::TriggerBroadcastRequest.new(broadcast_id: 1, payload: payload)

err_json = { meta: { error: "example error" } }.to_json

stub_request(:post, api_uri('/v1/campaigns/1/triggers'))
.with(headers: request_headers, body: req.payload)
.to_return(status: 400, body: err_json, headers: {})

lambda { client.trigger_broadcast(req) }.should(
raise_error(Customerio::InvalidResponse) { |error|
error.message.should eq "example error"
error.code.should eq "400"
}
)
end

it "handles other failures (5xx)" do
payload = {
data: { name: 'foo' },
emails: ['foo', 'bar'],
email_ignore_missing: true,
email_add_duplicates: true
}
req = Customerio::TriggerBroadcastRequest.new(broadcast_id: 1, payload: payload)

stub_request(:post, api_uri('/v1/campaigns/1/triggers'))
.with(headers: request_headers, body: req.payload)
.to_return(status: 500, body: "Server unavailable", headers: {})

lambda { client.trigger_broadcast(req) }.should(
raise_error(Customerio::InvalidResponse) { |error|
error.message.should eq "Server unavailable"
error.code.should eq "500"
}
)
end

it 'supports campaign triggers based on email fields' do
payload = {
data: { name: 'foo' },
emails: ['foo', 'bar'],
email_ignore_missing: true,
email_add_duplicates: true
}
req = Customerio::TriggerBroadcastRequest.new(broadcast_id: 1, payload: payload)

stub_request(:post, api_uri('/v1/campaigns/1/triggers'))
.with(headers: request_headers, body: req.payload)
.to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})


expect(client.trigger_broadcast(req)).to eq({ 'delivery_id' => 1 })
end

it 'supports campaign triggers based on id fields' do
payload = {
data: { name: 'foo' },
ids: [1, 2, 3],
id_ignore_missing: true
}
req = Customerio::TriggerBroadcastRequest.new(broadcast_id: 1, payload: payload)

stub_request(:post, api_uri('/v1/campaigns/1/triggers'))
.with(headers: request_headers, body: req.payload)
.to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})

expect(client.trigger_broadcast(req)).to eq({ 'delivery_id' => 1 })
end

it 'supports campaign triggers based on per user data' do
user_data = { id: 1, data: { name: 'foo' } }
payload = {
data: { name: 'foo' },
per_user_data: [user_data]
}
req = Customerio::TriggerBroadcastRequest.new(broadcast_id: 1, payload: payload)

stub_request(:post, api_uri('/v1/campaigns/1/triggers'))
.with(headers: request_headers, body: req.payload)
.to_return(status: 200, body: { delivery_id: 1 }.to_json, headers: {})

expect(client.trigger_broadcast(req)).to eq({ 'delivery_id' => 1 })
end
end
end