Skip to content

Commit

Permalink
Add RawNotification for WNS
Browse files Browse the repository at this point in the history
- The raw notification supports only json data, since the data model
  enforces hash.
- Improved readme for Windows setup.
  • Loading branch information
mseppae committed Feb 1, 2016
1 parent 3894d2b commit 7de1680
Show file tree
Hide file tree
Showing 15 changed files with 259 additions and 29 deletions.
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ n.save!

For more documentation on [ADM](https://developer.amazon.com/sdk/adm.html).

#### Windows Phone Notification Service
#### Windows Phone Notification Service (Windows Phone 8.0 and 7.x)

Uses the older [Windows Phone 8 Toast template](https://msdn.microsoft.com/en-us/library/windows/apps/jj662938(v=vs.105).aspx)

```ruby
app = Rpush::Wpns::App.new
Expand All @@ -133,6 +135,42 @@ n.data = {title:"MyApp", body:"Hello world", param:"user_param1"}
n.save!
```

#### Windows Notification Service (Windows 8.1, 10 Apps & Phone > 8.0)

Uses the more recent [Toast template](https://msdn.microsoft.com/en-us/library/windows/apps/xaml/mt631604.aspx)

The client_id here is the sid url as seen [here](https://msdn.microsoft.com/en-us/library/windows/apps/hh465407.aspx#7-SIDandSecret). Do not confuse it with the client_id on dashboard.

```ruby
app = Rpush::Wns::App.new
app.name = "windows_phone_app"
app.client_id = YOUR_SID_URL
app.client_secret = YOUR_CLIENT_SECRET
app.connections = 1
app.save!
```

```ruby
n = Rpush::Wns::Notification.new
n.app = Rpush::Wns::App.find_by_name("windows_phone_app")
n.uri = "http://..."
n.data = {title:"MyApp", body:"Hello world"}
n.save!
```

#### Windows Raw Push Notifications

Note: The data is passed as .to_json so only this format is supported, altough raw notifications are meant to support any kind of data.
Current data structure enforces hashes and to_json representation is natural presentation of it.

```ruby
n = Rpush::Wns::RawNotification.new
n.app = Rpush::Wns::App.find_by_name("windows_phone_app")
n.uri = 'http://...'
n.data = { foo: 'foo', bar: 'bar' }
n.save!
```

### Running Rpush

It is recommended to run Rpush as a separate process in most cases, though embedding and manual modes are provided for low-workload environments.
Expand Down
1 change: 1 addition & 0 deletions lib/rpush/client/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
require 'rpush/client/active_record/wpns/app'

require 'rpush/client/active_record/wns/notification'
require 'rpush/client/active_record/wns/raw_notification'
require 'rpush/client/active_record/wns/app'

require 'rpush/client/active_record/adm/notification'
Expand Down
13 changes: 13 additions & 0 deletions lib/rpush/client/active_record/wns/raw_notification.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Rpush
module Client
module ActiveRecord
module Wns
class RawNotification < Rpush::Client::ActiveRecord::Notification
validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator,
limit: 5000
include Rpush::Client::ActiveModel::Wns::Notification
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rpush/client/mongoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
require 'rpush/client/mongoid/wpns/app'

require 'rpush/client/mongoid/wns/notification'
require 'rpush/client/mongoid/wns/raw_notification'
require 'rpush/client/mongoid/wns/app'

require 'rpush/client/mongoid/adm/notification'
Expand Down
11 changes: 11 additions & 0 deletions lib/rpush/client/mongoid/wns/raw_notification.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Rpush
module Client
module Mongoid
module Wns
class RawNotification < Rpush::Client::Mongoid::Notification
include Rpush::Client::ActiveModel::Wns::Notification
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rpush/client/redis.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@

require 'rpush/client/redis/wns/app'
require 'rpush/client/redis/wns/notification'
require 'rpush/client/redis/wns/raw_notification'

Modis.configure do |config|
config.namespace = :rpush
Expand Down
11 changes: 11 additions & 0 deletions lib/rpush/client/redis/wns/raw_notification.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Rpush
module Client
module Redis
module Wns
class RawNotification < Rpush::Client::Redis::Notification
include Rpush::Client::ActiveModel::Wns::Notification
end
end
end
end
end
3 changes: 3 additions & 0 deletions lib/rpush/daemon.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
require 'rpush/daemon/wpns/delivery'
require 'rpush/daemon/wpns'

require 'rpush/daemon/wns/post_request'
require 'rpush/daemon/wns/raw_request'
require 'rpush/daemon/wns/toast_request'
require 'rpush/daemon/wns/delivery'
require 'rpush/daemon/wns'

Expand Down
30 changes: 2 additions & 28 deletions lib/rpush/daemon/wns/delivery.rb
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,12 @@ def retry_notification(reason)
end

def do_post
body = notification_to_xml
uri = URI.parse(@notification.uri)
post = Net::HTTP::Post.new(uri.request_uri,
"Content-Length" => body.length.to_s,
"Content-Type" => "text/xml",
"X-WNS-Type" => "wns/toast",
"X-WNS-RequestForStatus" => "true",
"Authorization" => "Bearer #{access_token}")
post.body = body
post = PostRequest.create(@notification, access_token)
@http.request(URI.parse(@notification.uri), post)
end

def status_from_response(response)
headers = response.to_hash.inject({}) {|h, v| h[v[0].downcase] = v[1]; h}
headers = response.to_hash.each_with_object({}) { |e, a| a[e[0].downcase] = e[1] }
{
notification: headers["x-wns-status"],
device_connection: headers["x-wns-deviceconnectionstatus"],
Expand All @@ -155,24 +147,6 @@ def status_from_response(response)
}
end

def notification_to_xml
title = clean_param_string(@notification.data['title']) if @notification.data['title'].present?
body = clean_param_string(@notification.data['body']) if @notification.data['body'].present?
"<toast>
<visual version='1' lang='en-US'>
<binding template='ToastText02'>
<text id='1'>#{title}</text>
<text id='2'>#{body}</text>
</binding>
</visual>
</toast>"
end

def clean_param_string(string)
string.gsub(/&/, "&amp;").gsub(/</, "&lt;") \
.gsub(/>/, "&gt;").gsub(/'/, "&apos;").gsub(/"/, "&quot;")
end

def access_token
if @notification.app.access_token.nil? || @notification.app.access_token_expired?
post = Net::HTTP::Post.new(WPN_TOKEN_URI.path, 'Content-Type' => 'application/x-www-form-urlencoded')
Expand Down
25 changes: 25 additions & 0 deletions lib/rpush/daemon/wns/post_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module Rpush
module Daemon
module Wns
class PostRequest
def self.create(notification, access_token)
is_raw_notification = lambda do |n|
n.class.name.match(/RawNotification/)
end

stringify_keys = lambda do |data|
data.keys.each { |key| data[key.to_s || key] = data.delete(key) }
end

stringify_keys.call(notification.data)

if is_raw_notification.call(notification)
RawRequest.create(notification, access_token)
else
ToastRequest.create(notification, access_token)
end
end
end
end
end
end
22 changes: 22 additions & 0 deletions lib/rpush/daemon/wns/raw_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module Rpush
module Daemon
module Wns
class RawRequest
def self.create(notification, access_token)
body = notification.data.to_json
uri = URI.parse(notification.uri)
post = Net::HTTP::Post.new(
uri.request_uri,
"Content-Length" => body.length.to_s,
"Content-Type" => "application/octet-stream",
"X-WNS-Type" => "wns/raw",
"X-WNS-RequestForStatus" => "true",
"Authorization" => "Bearer #{access_token}"
)
post.body = body
post
end
end
end
end
end
47 changes: 47 additions & 0 deletions lib/rpush/daemon/wns/toast_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Rpush
module Daemon
module Wns
class ToastRequest
def self.create(notification, access_token)
body = ToastRequestPayload.new(notification).to_xml
uri = URI.parse(notification.uri)
post = Net::HTTP::Post.new(
uri.request_uri,
"Content-Length" => body.length.to_s,
"Content-Type" => "text/xml",
"X-WNS-Type" => "wns/toast",
"X-WNS-RequestForStatus" => "true",
"Authorization" => "Bearer #{access_token}"
)
post.body = body
post
end
end

class ToastRequestPayload
def initialize(notification)
@title = notification.data['title'] || ''
@body = notification.data['body'] || ''
end

def to_xml
"<toast>
<visual version='1' lang='en-US'>
<binding template='ToastText02'>
<text id='1'>#{CleanParamString.clean(@title)}</text>
<text id='2'>#{CleanParamString.clean(@body)}</text>
</binding>
</visual>
</toast>"
end
end

class CleanParamString
def self.clean(string)
string.gsub(/&/, "&amp;").gsub(/</, "&lt;") \
.gsub(/>/, "&gt;").gsub(/'/, "&apos;").gsub(/"/, "&quot;")
end
end
end
end
end
26 changes: 26 additions & 0 deletions spec/unit/client/active_record/wns/raw_notification_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'unit_spec_helper'

describe Rpush::Client::ActiveRecord::Wns::RawNotification do
let(:notification) do
notif = Rpush::Client::ActiveRecord::Wns::RawNotification.new
notif.app = Rpush::Client::ActiveRecord::Wns::App.new(name: "aname")
notif.uri = 'https://db5.notify.windows.com/?token=TOKEN'
notif.data = { foo: 'foo', bar: 'bar' }
notif
end

it 'does not allow the size of payload over 5 KB' do
allow(notification).to receive(:payload_data_size) { 5001 }
expect(notification.valid?).to be(false)
end

it 'allows exact payload of 5 KB' do
allow(notification).to receive(:payload_data_size) { 5000 }
expect(notification.valid?).to be(true)
end

it 'allows the size of payload under 5 KB' do
allow(notification).to receive(:payload_data_size) { 4999 }
expect(notification.valid?).to be(true)
end
end if active_record?
5 changes: 5 additions & 0 deletions spec/unit/daemon/wns/delivery_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ def perform_with_rescue
expect(store).to receive(:update_app).with app
perform
end

it 'uses the PostRequest factory for creating the request' do
expect(Rpush::Daemon::Wns::PostRequest).to receive(:create).with(notification, "dummy_access_token")
perform
end
end

describe "an 200 response with a valid access token" do
Expand Down
52 changes: 52 additions & 0 deletions spec/unit/daemon/wns/post_request_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
require 'unit_spec_helper'

describe Rpush::Daemon::Wns::PostRequest do
let(:app) do
Rpush::Wns::App.create!(
name: "MyApp",
client_id: "someclient",
client_secret: "somesecret",
access_token: "access_token",
access_token_expiration: Time.now + (60 * 10)
)
end

context 'Notification' do
let(:notification) do
Rpush::Wns::Notification.create!(
app: app,
data: {
title: "MyApp",
body: "Example notification"
},
uri: "http://some.example/"
)
end

it 'creates a request characteristic for toast notification' do
request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
expect(request['X-WNS-Type']).to eq('wns/toast')
expect(request['Content-Type']).to eq('text/xml')
expect(request.body).to include('<toast>')
expect(request.body).to include('MyApp')
expect(request.body).to include('Example notification')
end
end

context 'RawNotification' do
let(:notification) do
Rpush::Wns::RawNotification.create!(
app: app,
data: { foo: 'foo', bar: 'bar' },
uri: "http://some.example/"
)
end

it 'creates a request characteristic for raw notification' do
request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
expect(request['X-WNS-Type']).to eq('wns/raw')
expect(request['Content-Type']).to eq('application/octet-stream')
expect(request.body).to eq("{\"foo\":\"foo\",\"bar\":\"bar\"}")
end
end
end

0 comments on commit 7de1680

Please sign in to comment.