rpush 2.6.0 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +21 -1
  3. data/README.md +48 -1
  4. data/lib/generators/rpush_migration_generator.rb +2 -1
  5. data/lib/generators/templates/rpush_2_7_0_updates.rb +12 -0
  6. data/lib/rpush/client/active_model/adm/notification.rb +1 -1
  7. data/lib/rpush/client/active_model/apns/notification.rb +2 -2
  8. data/lib/rpush/client/active_model/gcm/notification.rb +32 -5
  9. data/lib/rpush/client/active_model/wns/notification.rb +5 -1
  10. data/lib/rpush/client/active_record.rb +2 -0
  11. data/lib/rpush/client/active_record/notification.rb +12 -2
  12. data/lib/rpush/client/active_record/wns/badge_notification.rb +15 -0
  13. data/lib/rpush/client/active_record/wns/raw_notification.rb +13 -0
  14. data/lib/rpush/client/mongoid.rb +2 -0
  15. data/lib/rpush/client/mongoid/notification.rb +1 -0
  16. data/lib/rpush/client/mongoid/wns/badge_notification.rb +15 -0
  17. data/lib/rpush/client/mongoid/wns/raw_notification.rb +11 -0
  18. data/lib/rpush/client/redis.rb +2 -0
  19. data/lib/rpush/client/redis/notification.rb +1 -0
  20. data/lib/rpush/client/redis/wns/badge_notification.rb +15 -0
  21. data/lib/rpush/client/redis/wns/raw_notification.rb +11 -0
  22. data/lib/rpush/daemon.rb +4 -0
  23. data/lib/rpush/daemon/wns/badge_request.rb +32 -0
  24. data/lib/rpush/daemon/wns/delivery.rb +2 -28
  25. data/lib/rpush/daemon/wns/post_request.rb +33 -0
  26. data/lib/rpush/daemon/wns/raw_request.rb +22 -0
  27. data/lib/rpush/daemon/wns/toast_request.rb +54 -0
  28. data/lib/rpush/version.rb +1 -1
  29. data/spec/support/active_record_setup.rb +2 -1
  30. data/spec/unit/client/active_record/apns/notification_spec.rb +21 -0
  31. data/spec/unit/client/active_record/gcm/notification_spec.rb +28 -0
  32. data/spec/unit/client/active_record/wns/badge_notification_spec.rb +15 -0
  33. data/spec/unit/client/active_record/wns/raw_notification_spec.rb +26 -0
  34. data/spec/unit/daemon/wns/delivery_spec.rb +5 -0
  35. data/spec/unit/daemon/wns/post_request_spec.rb +117 -0
  36. data/spec/unit/notification_shared.rb +30 -12
  37. metadata +19 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6f3cc8682dd4d0be056e3fdac764d7e611f93d13
4
- data.tar.gz: 9d98d267c188c407324808d28d9a3922dbb9b060
3
+ metadata.gz: a1610a8d44461c0347fcc0d4475906e96c79f7d6
4
+ data.tar.gz: ef3c2b45c48acd439f6e928bfd87fa8c67a80ccb
5
5
  SHA512:
6
- metadata.gz: 59ee90a994a38a3684baef26510f1bf8befac593c91d691988f729224eeda34129c4965aa3bd8b7ed7db2b2c4288087bf11372c4089b73c90a77e78706fc56f4
7
- data.tar.gz: c6d83831032136982317afe65731d0c0f56c2c568ce8c5c27ad96e5cb0b573587615d09fa36da7791999e78c652f8f2b05808a1c266e67f19025e3cb9a3b8ba5
6
+ metadata.gz: 3477a221b69f267071e04d02b52b2d5914ad3d1a905c421490c654fc3b161592e08659a4cba15f7c6b105ca3a63d88fa7f8b60ef6fcce96608cc147a6653f932
7
+ data.tar.gz: 0952319256425cda18ae58a16580e3a8f0933b74bd2b9917065715002030c8c761efebbb1636b06a4d4354a8b53f3ef1fd467aa3a617408e1f1a5e4edbbe8549
@@ -1,4 +1,24 @@
1
- ## 2.6.0 (Jan 25, 2016)
1
+ ## 2.7.0 (February 9, 2016)
2
+
3
+ #### Features
4
+
5
+ * Added support for GCM priorities. ([#243](https://github.com/rpush/rpush/pull/243) by [@aried3r](https://github.com/aried3r))
6
+ * Added support for GCM notification payload ([#246](https://github.com/rpush/rpush/pull/246) by [@aried3r](https://github.com/aried3r))
7
+ * Added support for Windows Raw Notifications (in JSON form) ([#238](https://github.com/rpush/rpush/pull/238) by [@mseppae](https://github.com/mseppae))
8
+ * Added WNS badge notifications ([#247](https://github.com/rpush/rpush/pull/247) by [@wouterh](https://github.com/wouterh))
9
+ * Added the `launch` argument of WNS toast notifications ([#247](https://github.com/rpush/rpush/pull/247) by [@wouterh](https://github.com/wouterh))
10
+ * Added sound in WNS toast notifications ([#247](https://github.com/rpush/rpush/pull/247) by [@wouterh](https://github.com/wouterh))
11
+
12
+ #### Changes
13
+
14
+ * Change `alert` type from `string` to `text` in ActiveRecord to allow bigger alert dictionaries. ([#248](https://github.com/rpush/rpush/pull/248) by [@schmidt](https://github.com/schmidt))
15
+
16
+ #### Fixes
17
+
18
+ * Fixed issue where setting the `mdm` parameter broke `to_binary` for MDM APNs ([#234](https://github.com/rpush/rpush/pull/234) by [@troya2](https://github.com/troya2))
19
+ * Fixed `as_json` ([#231](https://github.com/rpush/rpush/issues/231) by [@aried3r](https://github.com/aried3r))
20
+
21
+ ## 2.6.0 (January 25, 2016)
2
22
 
3
23
  #### Features
4
24
 
data/README.md CHANGED
@@ -87,11 +87,20 @@ n = Rpush::Gcm::Notification.new
87
87
  n.app = Rpush::Gcm::App.find_by_name("android_app")
88
88
  n.registration_ids = ["..."]
89
89
  n.data = { message: "hi mom!" }
90
+ n.priority = 'high' # Optional, can be either 'normal' or 'high'
91
+ n.content_available = true # Optional
92
+ # Optional notification payload. See the reference below for more keys you can use!
93
+ n.notification = { body: 'great match!',
94
+ title: 'Portugal vs. Denmark',
95
+ icon: 'myicon'
96
+ }
90
97
  n.save!
91
98
  ```
92
99
 
93
100
  GCM also requires you to respond to [Canonical IDs](https://github.com/rpush/rpush/wiki/Canonical-IDs).
94
101
 
102
+ Check the [GCM reference](https://developers.google.com/cloud-messaging/http-server-ref#notification-payload-support) for what keys you can use and are available to you. **Note:** Not all are yet implemented in Rpush.
103
+
95
104
  #### Amazon Device Messaging
96
105
 
97
106
  ```ruby
@@ -114,7 +123,9 @@ n.save!
114
123
 
115
124
  For more documentation on [ADM](https://developer.amazon.com/sdk/adm.html).
116
125
 
117
- #### Windows Phone Notification Service
126
+ #### Windows Phone Notification Service (Windows Phone 8.0 and 7.x)
127
+
128
+ Uses the older [Windows Phone 8 Toast template](https://msdn.microsoft.com/en-us/library/windows/apps/jj662938(v=vs.105).aspx)
118
129
 
119
130
  ```ruby
120
131
  app = Rpush::Wpns::App.new
@@ -133,6 +144,42 @@ n.data = {title:"MyApp", body:"Hello world", param:"user_param1"}
133
144
  n.save!
134
145
  ```
135
146
 
147
+ #### Windows Notification Service (Windows 8.1, 10 Apps & Phone > 8.0)
148
+
149
+ Uses the more recent [Toast template](https://msdn.microsoft.com/en-us/library/windows/apps/xaml/mt631604.aspx)
150
+
151
+ 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.
152
+
153
+ ```ruby
154
+ app = Rpush::Wns::App.new
155
+ app.name = "windows_phone_app"
156
+ app.client_id = YOUR_SID_URL
157
+ app.client_secret = YOUR_CLIENT_SECRET
158
+ app.connections = 1
159
+ app.save!
160
+ ```
161
+
162
+ ```ruby
163
+ n = Rpush::Wns::Notification.new
164
+ n.app = Rpush::Wns::App.find_by_name("windows_phone_app")
165
+ n.uri = "http://..."
166
+ n.data = {title:"MyApp", body:"Hello world"}
167
+ n.save!
168
+ ```
169
+
170
+ #### Windows Raw Push Notifications
171
+
172
+ 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.
173
+ Current data structure enforces hashes and `.to_json` representation is natural presentation of it.
174
+
175
+ ```ruby
176
+ n = Rpush::Wns::RawNotification.new
177
+ n.app = Rpush::Wns::App.find_by_name("windows_phone_app")
178
+ n.uri = 'http://...'
179
+ n.data = { foo: 'foo', bar: 'bar' }
180
+ n.save!
181
+ ```
182
+
136
183
  ### Running Rpush
137
184
 
138
185
  It is recommended to run Rpush as a separate process in most cases, though embedding and manual modes are provided for low-workload environments.
@@ -41,6 +41,7 @@ class RpushMigrationGenerator < Rails::Generators::Base
41
41
  add_rpush_migration('rpush_2_0_0_updates')
42
42
  add_rpush_migration('rpush_2_1_0_updates')
43
43
  add_rpush_migration('rpush_2_6_0_updates')
44
+ add_rpush_migration('rpush_2_7_0_updates')
44
45
  end
45
46
 
46
47
  protected
@@ -48,7 +49,7 @@ class RpushMigrationGenerator < Rails::Generators::Base
48
49
  def has_migration?(template)
49
50
  migration_dir = File.expand_path('db/migrate')
50
51
  self.class.migration_exists?(migration_dir, template)
51
- end
52
+ end
52
53
 
53
54
  def add_rpush_migration(template)
54
55
  self.class.next_template = template
@@ -0,0 +1,12 @@
1
+ class Rpush270Updates < ActiveRecord::Migration
2
+ def self.up
3
+ change_column :rpush_notifications, :alert, :text
4
+ add_column :rpush_notifications, :notification, :text
5
+ end
6
+
7
+ def self.down
8
+ change_column :rpush_notifications, :alert, :string
9
+ remove_column :rpush_notifications, :notification
10
+ end
11
+ end
12
+
@@ -14,7 +14,7 @@ module Rpush
14
14
  end
15
15
  end
16
16
 
17
- def as_json
17
+ def as_json(options = nil)
18
18
  json = { 'data' => data }
19
19
  json['consolidationKey'] = collapse_key if collapse_key
20
20
  # number of seconds before message is expired
@@ -38,7 +38,7 @@ module Rpush
38
38
  self.data = (data || {}).merge(CONTENT_AVAILABLE_KEY => true)
39
39
  end
40
40
 
41
- def as_json # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
41
+ def as_json(options = nil) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
42
42
  json = ActiveSupport::OrderedHash.new
43
43
 
44
44
  if data && data.key?(MDM_KEY)
@@ -80,7 +80,7 @@ module Rpush
80
80
 
81
81
  def priority_for_frame
82
82
  # It is an error to use APNS_PRIORITY_IMMEDIATE for a notification that only contains content-available.
83
- if as_json['aps'].keys == ['content-available']
83
+ if as_json['aps'].try(:keys) == ['content-available']
84
84
  APNS_PRIORITY_CONSERVE_POWER
85
85
  else
86
86
  priority || APNS_PRIORITY_IMMEDIATE
@@ -3,9 +3,14 @@ module Rpush
3
3
  module ActiveModel
4
4
  module Gcm
5
5
  module Notification
6
+ GCM_PRIORITY_HIGH = Rpush::Client::ActiveModel::Apns::Notification::APNS_PRIORITY_IMMEDIATE
7
+ GCM_PRIORITY_NORMAL = Rpush::Client::ActiveModel::Apns::Notification::APNS_PRIORITY_CONSERVE_POWER
8
+ GCM_PRIORITIES = [GCM_PRIORITY_HIGH, GCM_PRIORITY_NORMAL]
9
+
6
10
  def self.included(base)
7
11
  base.instance_eval do
8
12
  validates :registration_ids, presence: true
13
+ validates :priority, inclusion: { in: GCM_PRIORITIES }, allow_nil: true
9
14
 
10
15
  validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator, limit: 4096
11
16
  validates_with Rpush::Client::ActiveModel::RegistrationIdsCountValidator, limit: 1000
@@ -14,17 +19,39 @@ module Rpush
14
19
  end
15
20
  end
16
21
 
17
- def as_json
22
+ # This is a hack. The schema defines `priority` to be an integer, but GCM expects a string.
23
+ # But for users of rpush to have an API they might expect (setting priority to `high`, not 10)
24
+ # we do a little conversion here.
25
+ # I'm not happy about it, but this will have to do until I can take a further look.
26
+ def priority=(priority)
27
+ case priority
28
+ when 'high'
29
+ super(GCM_PRIORITY_HIGH)
30
+ when 'normal'
31
+ super(GCM_PRIORITY_NORMAL)
32
+ else
33
+ errors.add(:priority, 'must be one of either "normal" or "high"')
34
+ end
35
+ end
36
+
37
+ def as_json(options = nil)
18
38
  json = {
19
- 'registration_ids' => registration_ids,
20
- 'delay_while_idle' => delay_while_idle,
21
- 'data' => data
39
+ 'registration_ids' => registration_ids,
40
+ 'delay_while_idle' => delay_while_idle,
41
+ 'data' => data
22
42
  }
23
43
  json['collapse_key'] = collapse_key if collapse_key
24
- json['time_to_live'] = expiry if expiry
25
44
  json['content_available'] = content_available if content_available
45
+ json['notification'] = notification if notification
46
+ json['priority'] = priority_for_notification if priority
47
+ json['time_to_live'] = expiry if expiry
26
48
  json
27
49
  end
50
+
51
+ def priority_for_notification
52
+ return 'high' if priority == GCM_PRIORITY_HIGH
53
+ 'normal' if priority == GCM_PRIORITY_NORMAL
54
+ end
28
55
  end
29
56
  end
30
57
  end
@@ -10,6 +10,10 @@ module Rpush
10
10
  data['title'] = value
11
11
  self.data = data
12
12
  end
13
+
14
+ def skip_data_validation?
15
+ false
16
+ end
13
17
  end
14
18
 
15
19
  def self.included(base)
@@ -18,7 +22,7 @@ module Rpush
18
22
 
19
23
  validates :uri, presence: true
20
24
  validates :uri, format: { with: %r{https?://[\S]+} }
21
- validates :data, presence: true
25
+ validates :data, presence: true, unless: :skip_data_validation?
22
26
  end
23
27
  end
24
28
  end
@@ -16,6 +16,8 @@ require 'rpush/client/active_record/wpns/notification'
16
16
  require 'rpush/client/active_record/wpns/app'
17
17
 
18
18
  require 'rpush/client/active_record/wns/notification'
19
+ require 'rpush/client/active_record/wns/raw_notification'
20
+ require 'rpush/client/active_record/wns/badge_notification'
19
21
  require 'rpush/client/active_record/wns/app'
20
22
 
21
23
  require 'rpush/client/active_record/adm/notification'
@@ -16,15 +16,21 @@ module Rpush
16
16
  attr_accessible :badge, :device_token, :sound, :alert, :data, :expiry, :delivered,
17
17
  :delivered_at, :failed, :failed_at, :error_code, :error_description, :deliver_after,
18
18
  :alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids,
19
- :uri, :url_args, :category, :content_available
19
+ :uri, :url_args, :category, :content_available, :notification
20
20
  end
21
21
 
22
22
  def data=(attrs)
23
23
  return unless attrs
24
- fail ArgumentError, "must be a Hash" unless attrs.is_a?(Hash)
24
+ fail ArgumentError, 'must be a Hash' unless attrs.is_a?(Hash)
25
25
  write_attribute(:data, multi_json_dump(attrs.merge(data || {})))
26
26
  end
27
27
 
28
+ def notification=(attrs)
29
+ return unless attrs
30
+ fail ArgumentError, 'must be a Hash' unless attrs.is_a?(Hash)
31
+ write_attribute(:notification, multi_json_dump(attrs.merge(data || {})))
32
+ end
33
+
28
34
  def registration_ids=(ids)
29
35
  ids = [ids] if ids && !ids.is_a?(Array)
30
36
  super
@@ -33,6 +39,10 @@ module Rpush
33
39
  def data
34
40
  multi_json_load(read_attribute(:data)) if read_attribute(:data)
35
41
  end
42
+
43
+ def notification
44
+ multi_json_load(read_attribute(:notification)) if read_attribute(:notification)
45
+ end
36
46
  end
37
47
  end
38
48
  end
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Wns
5
+ class BadgeNotification < Rpush::Client::ActiveRecord::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+
8
+ def skip_data_validation?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ module Rpush
2
+ module Client
3
+ module ActiveRecord
4
+ module Wns
5
+ class RawNotification < Rpush::Client::ActiveRecord::Notification
6
+ validates_with Rpush::Client::ActiveModel::PayloadDataSizeValidator,
7
+ limit: 5120
8
+ include Rpush::Client::ActiveModel::Wns::Notification
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -28,6 +28,8 @@ require 'rpush/client/mongoid/wpns/notification'
28
28
  require 'rpush/client/mongoid/wpns/app'
29
29
 
30
30
  require 'rpush/client/mongoid/wns/notification'
31
+ require 'rpush/client/mongoid/wns/raw_notification'
32
+ require 'rpush/client/mongoid/wns/badge_notification'
31
33
  require 'rpush/client/mongoid/wns/app'
32
34
 
33
35
  require 'rpush/client/mongoid/adm/notification'
@@ -33,6 +33,7 @@ module Rpush
33
33
  field :url_args, type: Array
34
34
  field :category, type: String
35
35
  field :content_available, type: Boolean, default: false
36
+ field :notification, type: Hash
36
37
 
37
38
  field :integer_id, type: Integer
38
39
  increments :integer_id, model_name: name
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module Mongoid
4
+ module Wns
5
+ class BadgeNotification < Rpush::Client::Mongoid::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+
8
+ def skip_data_validation?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Client
3
+ module Mongoid
4
+ module Wns
5
+ class RawNotification < Rpush::Client::Mongoid::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -32,6 +32,8 @@ require 'rpush/client/redis/wpns/notification'
32
32
 
33
33
  require 'rpush/client/redis/wns/app'
34
34
  require 'rpush/client/redis/wns/notification'
35
+ require 'rpush/client/redis/wns/raw_notification'
36
+ require 'rpush/client/redis/wns/badge_notification'
35
37
 
36
38
  Modis.configure do |config|
37
39
  config.namespace = :rpush
@@ -43,6 +43,7 @@ module Rpush
43
43
  attribute :url_args, :array
44
44
  attribute :category, :string
45
45
  attribute :content_available, :boolean, default: false
46
+ attribute :notification, :hash
46
47
 
47
48
  def app
48
49
  return nil unless app_id
@@ -0,0 +1,15 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Wns
5
+ class BadgeNotification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+
8
+ def skip_data_validation?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ module Rpush
2
+ module Client
3
+ module Redis
4
+ module Wns
5
+ class RawNotification < Rpush::Client::Redis::Notification
6
+ include Rpush::Client::ActiveModel::Wns::Notification
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -46,6 +46,10 @@ require 'rpush/daemon/gcm'
46
46
  require 'rpush/daemon/wpns/delivery'
47
47
  require 'rpush/daemon/wpns'
48
48
 
49
+ require 'rpush/daemon/wns/post_request'
50
+ require 'rpush/daemon/wns/raw_request'
51
+ require 'rpush/daemon/wns/toast_request'
52
+ require 'rpush/daemon/wns/badge_request'
49
53
  require 'rpush/daemon/wns/delivery'
50
54
  require 'rpush/daemon/wns'
51
55
 
@@ -0,0 +1,32 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Wns
4
+ class BadgeRequest
5
+ def self.create(notification, access_token)
6
+ body = BadgeRequestPayload.new(notification).to_xml
7
+ uri = URI.parse(notification.uri)
8
+ post = Net::HTTP::Post.new(
9
+ uri.request_uri,
10
+ "Content-Length" => body.length.to_s,
11
+ "Content-Type" => "text/xml",
12
+ "X-WNS-Type" => "wns/badge",
13
+ "X-WNS-RequestForStatus" => "true",
14
+ "Authorization" => "Bearer #{access_token}"
15
+ )
16
+ post.body = body
17
+ post
18
+ end
19
+ end
20
+
21
+ class BadgeRequestPayload
22
+ def initialize(notification)
23
+ @badge = notification.badge || 0
24
+ end
25
+
26
+ def to_xml
27
+ "<badge value=\"#{@badge}\"/>"
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -132,20 +132,12 @@ module Rpush
132
132
  end
133
133
 
134
134
  def do_post
135
- body = notification_to_xml
136
- uri = URI.parse(@notification.uri)
137
- post = Net::HTTP::Post.new(uri.request_uri,
138
- "Content-Length" => body.length.to_s,
139
- "Content-Type" => "text/xml",
140
- "X-WNS-Type" => "wns/toast",
141
- "X-WNS-RequestForStatus" => "true",
142
- "Authorization" => "Bearer #{access_token}")
143
- post.body = body
135
+ post = PostRequest.create(@notification, access_token)
144
136
  @http.request(URI.parse(@notification.uri), post)
145
137
  end
146
138
 
147
139
  def status_from_response(response)
148
- headers = response.to_hash.inject({}) {|h, v| h[v[0].downcase] = v[1]; h}
140
+ headers = response.to_hash.each_with_object({}) { |e, a| a[e[0].downcase] = e[1] }
149
141
  {
150
142
  notification: headers["x-wns-status"],
151
143
  device_connection: headers["x-wns-deviceconnectionstatus"],
@@ -155,24 +147,6 @@ module Rpush
155
147
  }
156
148
  end
157
149
 
158
- def notification_to_xml
159
- title = clean_param_string(@notification.data['title']) if @notification.data['title'].present?
160
- body = clean_param_string(@notification.data['body']) if @notification.data['body'].present?
161
- "<toast>
162
- <visual version='1' lang='en-US'>
163
- <binding template='ToastText02'>
164
- <text id='1'>#{title}</text>
165
- <text id='2'>#{body}</text>
166
- </binding>
167
- </visual>
168
- </toast>"
169
- end
170
-
171
- def clean_param_string(string)
172
- string.gsub(/&/, "&amp;").gsub(/</, "&lt;") \
173
- .gsub(/>/, "&gt;").gsub(/'/, "&apos;").gsub(/"/, "&quot;")
174
- end
175
-
176
150
  def access_token
177
151
  if @notification.app.access_token.nil? || @notification.app.access_token_expired?
178
152
  post = Net::HTTP::Post.new(WPN_TOKEN_URI.path, 'Content-Type' => 'application/x-www-form-urlencoded')
@@ -0,0 +1,33 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Wns
4
+ class PostRequest
5
+ def self.create(notification, access_token)
6
+ stringify_keys(notification.data) unless notification.data.nil?
7
+
8
+ if raw_notification?(notification)
9
+ RawRequest.create(notification, access_token)
10
+ elsif badge_notification?(notification)
11
+ BadgeRequest.create(notification, access_token)
12
+ else
13
+ ToastRequest.create(notification, access_token)
14
+ end
15
+ end
16
+
17
+ private_class_method
18
+
19
+ def self.raw_notification?(notification)
20
+ notification.class.name.match(/RawNotification/)
21
+ end
22
+
23
+ def self.badge_notification?(notification)
24
+ notification.class.name.match(/BadgeNotification/)
25
+ end
26
+
27
+ def self.stringify_keys(data)
28
+ data.keys.each { |key| data[key.to_s || key] = data.delete(key) }
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Wns
4
+ class RawRequest
5
+ def self.create(notification, access_token)
6
+ body = notification.data.to_json
7
+ uri = URI.parse(notification.uri)
8
+ post = Net::HTTP::Post.new(
9
+ uri.request_uri,
10
+ "Content-Length" => body.length.to_s,
11
+ "Content-Type" => "application/octet-stream",
12
+ "X-WNS-Type" => "wns/raw",
13
+ "X-WNS-RequestForStatus" => "true",
14
+ "Authorization" => "Bearer #{access_token}"
15
+ )
16
+ post.body = body
17
+ post
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Wns
4
+ class ToastRequest
5
+ def self.create(notification, access_token)
6
+ body = ToastRequestPayload.new(notification).to_xml
7
+ uri = URI.parse(notification.uri)
8
+ post = Net::HTTP::Post.new(
9
+ uri.request_uri,
10
+ "Content-Length" => body.length.to_s,
11
+ "Content-Type" => "text/xml",
12
+ "X-WNS-Type" => "wns/toast",
13
+ "X-WNS-RequestForStatus" => "true",
14
+ "Authorization" => "Bearer #{access_token}"
15
+ )
16
+ post.body = body
17
+ post
18
+ end
19
+ end
20
+
21
+ class ToastRequestPayload
22
+ def initialize(notification)
23
+ @title = notification.data['title'] || ''
24
+ @body = notification.data['body'] || ''
25
+ @launch = notification.data['launch']
26
+ @sound = notification.sound unless notification.sound.eql?("default".freeze)
27
+ end
28
+
29
+ def to_xml
30
+ launch_string = "" unless @launch
31
+ launch_string = " launch='#{CleanParamString.clean(@launch)}'" if @launch
32
+ audio_string = "" unless @sound
33
+ audio_string = "<audio src='#{CleanParamString.clean(@sound)}'/>" if @sound
34
+ "<toast#{launch_string}>
35
+ <visual version='1' lang='en-US'>
36
+ <binding template='ToastText02'>
37
+ <text id='1'>#{CleanParamString.clean(@title)}</text>
38
+ <text id='2'>#{CleanParamString.clean(@body)}</text>
39
+ </binding>
40
+ </visual>
41
+ #{audio_string}
42
+ </toast>"
43
+ end
44
+ end
45
+
46
+ class CleanParamString
47
+ def self.clean(string)
48
+ string.gsub(/&/, "&amp;").gsub(/</, "&lt;") \
49
+ .gsub(/>/, "&gt;").gsub(/'/, "&apos;").gsub(/"/, "&quot;")
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,3 +1,3 @@
1
1
  module Rpush
2
- VERSION = '2.6.0'
2
+ VERSION = '2.7.0'
3
3
  end
@@ -30,8 +30,9 @@ require 'generators/templates/add_rpush'
30
30
  require 'generators/templates/rpush_2_0_0_updates'
31
31
  require 'generators/templates/rpush_2_1_0_updates'
32
32
  require 'generators/templates/rpush_2_6_0_updates'
33
+ require 'generators/templates/rpush_2_7_0_updates'
33
34
 
34
- migrations = [AddRpush, Rpush200Updates, Rpush210Updates, Rpush260Updates]
35
+ migrations = [AddRpush, Rpush200Updates, Rpush210Updates, Rpush260Updates, Rpush270Updates]
35
36
 
36
37
  unless ENV['TRAVIS']
37
38
  migrations.reverse_each do |m|
@@ -23,6 +23,17 @@ describe Rpush::Client::ActiveRecord::Apns::Notification do
23
23
  expect(notification.errors[:base].include?("APN notification cannot be larger than 2048 bytes. Try condensing your alert and device attributes.")).to be_truthy
24
24
  end
25
25
 
26
+ it "should store long alerts" do
27
+ notification.app = app
28
+ notification.device_token = "a" * 64
29
+ notification.alert = "*" * 300
30
+ expect(notification.valid?).to be_truthy
31
+
32
+ notification.save!
33
+ notification.reload
34
+ expect(notification.alert).to eq("*" * 300)
35
+ end
36
+
26
37
  it "should default the sound to 'default'" do
27
38
  expect(notification.sound).to eq('default')
28
39
  end
@@ -98,6 +109,11 @@ describe Rpush::Client::ActiveRecord::Apns::Notification, 'MDM' do
98
109
  let(:magic) { 'abc123' }
99
110
  let(:notification) { Rpush::Client::ActiveRecord::Apns::Notification.new }
100
111
 
112
+ before do
113
+ notification.device_token = "a" * 64
114
+ notification.id = 1234
115
+ end
116
+
101
117
  it 'includes the mdm magic in the payload' do
102
118
  notification.mdm = magic
103
119
  expect(notification.as_json).to eq('mdm' => magic)
@@ -108,6 +124,11 @@ describe Rpush::Client::ActiveRecord::Apns::Notification, 'MDM' do
108
124
  notification.mdm = magic
109
125
  expect(notification.as_json.key?('aps')).to be_falsey
110
126
  end
127
+
128
+ it 'can be converted to binary' do
129
+ notification.mdm = magic
130
+ expect(notification.to_binary).to be_present
131
+ end
111
132
  end if active_record?
112
133
 
113
134
  describe Rpush::Client::ActiveRecord::Apns::Notification, 'content-available' do
@@ -36,4 +36,32 @@ describe Rpush::Client::ActiveRecord::Gcm::Notification do
36
36
  notification.content_available = true
37
37
  expect(notification.as_json['content_available']).to eq true
38
38
  end
39
+
40
+ it 'sets the priority to high when set to high' do
41
+ notification.priority = 'high'
42
+ expect(notification.as_json['priority']).to eq 'high'
43
+ end
44
+
45
+ it 'sets the priority to normal when set to normal' do
46
+ notification.priority = 'normal'
47
+ expect(notification.as_json['priority']).to eq 'normal'
48
+ end
49
+
50
+ it 'validates the priority is either "normal" or "high"' do
51
+ notification.priority = 'invalid'
52
+ expect(notification.errors[:priority]).to eq ['must be one of either "normal" or "high"']
53
+ end
54
+
55
+ it 'excludes the priority if it is not defined' do
56
+ expect(notification.as_json).not_to have_key 'priority'
57
+ end
58
+
59
+ it 'includes the notification payload if defined' do
60
+ notification.notification = { key: 'any key is allowed' }
61
+ expect(notification.as_json).to have_key 'notification'
62
+ end
63
+
64
+ it 'excludes the notification payload if undefined' do
65
+ expect(notification.as_json).not_to have_key 'notification'
66
+ end
39
67
  end if active_record?
@@ -0,0 +1,15 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::ActiveRecord::Wns::BadgeNotification do
4
+ let(:notification) do
5
+ notif = Rpush::Client::ActiveRecord::Wns::BadgeNotification.new
6
+ notif.app = Rpush::Client::ActiveRecord::Wns::App.new(name: "aname")
7
+ notif.uri = 'https://db5.notify.windows.com/?token=TOKEN'
8
+ notif.badge = 42
9
+ notif
10
+ end
11
+
12
+ it 'should allow a notification without data' do
13
+ expect(notification.valid?).to be(true)
14
+ end
15
+ end if active_record?
@@ -0,0 +1,26 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Client::ActiveRecord::Wns::RawNotification do
4
+ let(:notification) do
5
+ notif = Rpush::Client::ActiveRecord::Wns::RawNotification.new
6
+ notif.app = Rpush::Client::ActiveRecord::Wns::App.new(name: "aname")
7
+ notif.uri = 'https://db5.notify.windows.com/?token=TOKEN'
8
+ notif.data = { foo: 'foo', bar: 'bar' }
9
+ notif
10
+ end
11
+
12
+ it 'does not allow the size of payload over 5 KB' do
13
+ allow(notification).to receive(:payload_data_size) { 5121 }
14
+ expect(notification.valid?).to be(false)
15
+ end
16
+
17
+ it 'allows exact payload of 5 KB' do
18
+ allow(notification).to receive(:payload_data_size) { 5120 }
19
+ expect(notification.valid?).to be(true)
20
+ end
21
+
22
+ it 'allows the size of payload under 5 KB' do
23
+ allow(notification).to receive(:payload_data_size) { 5119 }
24
+ expect(notification.valid?).to be(true)
25
+ end
26
+ end if active_record?
@@ -54,6 +54,11 @@ describe Rpush::Daemon::Wns::Delivery do
54
54
  expect(store).to receive(:update_app).with app
55
55
  perform
56
56
  end
57
+
58
+ it 'uses the PostRequest factory for creating the request' do
59
+ expect(Rpush::Daemon::Wns::PostRequest).to receive(:create).with(notification, "dummy_access_token")
60
+ perform
61
+ end
57
62
  end
58
63
 
59
64
  describe "an 200 response with a valid access token" do
@@ -0,0 +1,117 @@
1
+ require 'unit_spec_helper'
2
+
3
+ describe Rpush::Daemon::Wns::PostRequest do
4
+ let(:app) do
5
+ Rpush::Wns::App.create!(
6
+ name: "MyApp",
7
+ client_id: "someclient",
8
+ client_secret: "somesecret",
9
+ access_token: "access_token",
10
+ access_token_expiration: Time.now + (60 * 10)
11
+ )
12
+ end
13
+
14
+ context 'Notification' do
15
+ let(:notification) do
16
+ Rpush::Wns::Notification.create!(
17
+ app: app,
18
+ data: {
19
+ title: "MyApp",
20
+ body: "Example notification"
21
+ },
22
+ uri: "http://some.example/"
23
+ )
24
+ end
25
+
26
+ it 'creates a request characteristic for toast notification' do
27
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
28
+ expect(request['X-WNS-Type']).to eq('wns/toast')
29
+ expect(request['Content-Type']).to eq('text/xml')
30
+ expect(request.body).to include('<toast>')
31
+ expect(request.body).to include('MyApp')
32
+ expect(request.body).to include('Example notification')
33
+ end
34
+
35
+ context 'with launch' do
36
+ let(:notification) do
37
+ Rpush::Wns::Notification.create!(
38
+ app: app,
39
+ data: {
40
+ title: "MyApp",
41
+ body: "Example notification",
42
+ launch: "MyLaunchArgument"
43
+ },
44
+ uri: "http://some.example/"
45
+ )
46
+ end
47
+
48
+ it 'creates a request characteristic for toast notification with launch' do
49
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
50
+ expect(request['X-WNS-Type']).to eq('wns/toast')
51
+ expect(request['Content-Type']).to eq('text/xml')
52
+ expect(request.body).to include("<toast launch='MyLaunchArgument'>")
53
+ expect(request.body).to include('MyApp')
54
+ expect(request.body).to include('Example notification')
55
+ end
56
+ end
57
+
58
+ context 'with sound' do
59
+ let(:notification) do
60
+ Rpush::Wns::Notification.create!(
61
+ app: app,
62
+ data: {
63
+ title: "MyApp",
64
+ body: "Example notification"
65
+ },
66
+ uri: "http://some.example/",
67
+ sound: "ms-appx:///examplesound.wav"
68
+ )
69
+ end
70
+
71
+ it 'creates a request characteristic for toast notification' do
72
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
73
+ expect(request['X-WNS-Type']).to eq('wns/toast')
74
+ expect(request['Content-Type']).to eq('text/xml')
75
+ expect(request.body).to include('<toast>')
76
+ expect(request.body).to include('MyApp')
77
+ expect(request.body).to include('Example notification')
78
+ expect(request.body).to include("<audio src='ms-appx:///examplesound.wav'/>")
79
+ end
80
+ end
81
+ end
82
+
83
+ context 'RawNotification' do
84
+ let(:notification) do
85
+ Rpush::Wns::RawNotification.create!(
86
+ app: app,
87
+ data: { foo: 'foo', bar: 'bar' },
88
+ uri: "http://some.example/"
89
+ )
90
+ end
91
+
92
+ it 'creates a request characteristic for raw notification' do
93
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
94
+ expect(request['X-WNS-Type']).to eq('wns/raw')
95
+ expect(request['Content-Type']).to eq('application/octet-stream')
96
+ expect(request.body).to eq("{\"foo\":\"foo\",\"bar\":\"bar\"}")
97
+ end
98
+ end
99
+
100
+ context 'BadgeNotification' do
101
+ let(:notification) do
102
+ Rpush::Wns::BadgeNotification.create!(
103
+ app: app,
104
+ uri: "http://some.example/",
105
+ badge: 42
106
+ )
107
+ end
108
+
109
+ it 'creates a request characteristic for badge notification' do
110
+ request = Rpush::Daemon::Wns::PostRequest.create(notification, 'token')
111
+ expect(request['X-WNS-Type']).to eq('wns/badge')
112
+ expect(request['Content-Type']).to eq('text/xml')
113
+ expect(request.body).to include('<badge')
114
+ expect(request.body).to include('42')
115
+ end
116
+ end
117
+ end
@@ -1,35 +1,53 @@
1
- shared_examples_for "an Notification subclass" do
2
- describe "when assigning data for the device" do
1
+ shared_examples_for 'an Notification subclass' do
2
+ describe 'when assigning data for the device' do
3
3
  before { allow(Rpush::Deprecation).to receive(:warn) }
4
4
 
5
- it "calls MultiJson.dump when multi_json responds to :dump" do
5
+ it 'calls MultiJson.dump when multi_json responds to :dump' do
6
6
  notification = notification_class.new
7
7
  allow(MultiJson).to receive(:respond_to?).with(:dump).and_return(true)
8
8
  expect(MultiJson).to receive(:dump).with(any_args)
9
9
  notification.data = { pirates: 1 }
10
10
  end
11
11
 
12
- it "calls MultiJson.encode when multi_json does not respond to :dump" do
12
+ it 'calls MultiJson.encode when multi_json does not respond to :dump' do
13
13
  notification = notification_class.new
14
14
  allow(MultiJson).to receive(:respond_to?).with(:dump).and_return(false)
15
15
  expect(MultiJson).to receive(:encode).with(any_args)
16
16
  notification.data = { ninjas: 1 }
17
17
  end
18
18
 
19
- it "raises an ArgumentError if something other than a Hash is assigned" do
19
+ it 'raises an ArgumentError if something other than a Hash is assigned' do
20
20
  expect do
21
21
  notification.data = []
22
- end.to raise_error(ArgumentError, "must be a Hash")
22
+ end.to raise_error(ArgumentError, 'must be a Hash')
23
23
  end
24
24
 
25
- it "encodes the given Hash as JSON" do
26
- notification.data = { hi: "mom" }
27
- expect(notification.read_attribute(:data)).to eq("{\"hi\":\"mom\"}")
25
+ it 'encodes the given Hash as JSON' do
26
+ notification.data = { hi: 'mom'}
27
+ expect(notification.read_attribute(:data)).to eq('{"hi":"mom"}')
28
28
  end
29
29
 
30
- it "decodes the JSON when using the reader method" do
31
- notification.data = { hi: "mom" }
32
- expect(notification.data).to eq("hi" => "mom")
30
+ it 'decodes the JSON when using the reader method' do
31
+ notification.data = { hi: 'mom'}
32
+ expect(notification.data).to eq('hi' => 'mom')
33
+ end
34
+ end
35
+
36
+ describe 'when assigning the notification payload for the device' do
37
+ it 'raises an ArgumentError if something other than a Hash is assigned' do
38
+ expect do
39
+ notification.notification = []
40
+ end.to raise_error(ArgumentError, 'must be a Hash')
41
+ end
42
+
43
+ it 'encodes the given Hash as JSON' do
44
+ notification.notification = { hi: 'dad'}
45
+ expect(notification.read_attribute(:notification)).to eq('{"hi":"dad"}')
46
+ end
47
+
48
+ it 'decodes the JSON when using the reader method' do
49
+ notification.notification = { hi: 'dad'}
50
+ expect(notification.notification).to eq('hi' => 'dad')
33
51
  end
34
52
  end
35
53
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rpush
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.6.0
4
+ version: 2.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ian Leitch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-25 00:00:00.000000000 Z
11
+ date: 2016-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -129,6 +129,7 @@ files:
129
129
  - lib/generators/templates/rpush_2_0_0_updates.rb
130
130
  - lib/generators/templates/rpush_2_1_0_updates.rb
131
131
  - lib/generators/templates/rpush_2_6_0_updates.rb
132
+ - lib/generators/templates/rpush_2_7_0_updates.rb
132
133
  - lib/rpush.rb
133
134
  - lib/rpush/apns_feedback.rb
134
135
  - lib/rpush/cli.rb
@@ -161,7 +162,9 @@ files:
161
162
  - lib/rpush/client/active_record/gcm/notification.rb
162
163
  - lib/rpush/client/active_record/notification.rb
163
164
  - lib/rpush/client/active_record/wns/app.rb
165
+ - lib/rpush/client/active_record/wns/badge_notification.rb
164
166
  - lib/rpush/client/active_record/wns/notification.rb
167
+ - lib/rpush/client/active_record/wns/raw_notification.rb
165
168
  - lib/rpush/client/active_record/wpns/app.rb
166
169
  - lib/rpush/client/active_record/wpns/notification.rb
167
170
  - lib/rpush/client/mongoid.rb
@@ -175,7 +178,9 @@ files:
175
178
  - lib/rpush/client/mongoid/gcm/notification.rb
176
179
  - lib/rpush/client/mongoid/notification.rb
177
180
  - lib/rpush/client/mongoid/wns/app.rb
181
+ - lib/rpush/client/mongoid/wns/badge_notification.rb
178
182
  - lib/rpush/client/mongoid/wns/notification.rb
183
+ - lib/rpush/client/mongoid/wns/raw_notification.rb
179
184
  - lib/rpush/client/mongoid/wpns/app.rb
180
185
  - lib/rpush/client/mongoid/wpns/notification.rb
181
186
  - lib/rpush/client/redis.rb
@@ -189,7 +194,9 @@ files:
189
194
  - lib/rpush/client/redis/gcm/notification.rb
190
195
  - lib/rpush/client/redis/notification.rb
191
196
  - lib/rpush/client/redis/wns/app.rb
197
+ - lib/rpush/client/redis/wns/badge_notification.rb
192
198
  - lib/rpush/client/redis/wns/notification.rb
199
+ - lib/rpush/client/redis/wns/raw_notification.rb
193
200
  - lib/rpush/client/redis/wpns/app.rb
194
201
  - lib/rpush/client/redis/wpns/notification.rb
195
202
  - lib/rpush/configuration.rb
@@ -233,7 +240,11 @@ files:
233
240
  - lib/rpush/daemon/synchronizer.rb
234
241
  - lib/rpush/daemon/tcp_connection.rb
235
242
  - lib/rpush/daemon/wns.rb
243
+ - lib/rpush/daemon/wns/badge_request.rb
236
244
  - lib/rpush/daemon/wns/delivery.rb
245
+ - lib/rpush/daemon/wns/post_request.rb
246
+ - lib/rpush/daemon/wns/raw_request.rb
247
+ - lib/rpush/daemon/wns/toast_request.rb
237
248
  - lib/rpush/daemon/wpns.rb
238
249
  - lib/rpush/daemon/wpns/delivery.rb
239
250
  - lib/rpush/deprecatable.rb
@@ -280,6 +291,8 @@ files:
280
291
  - spec/unit/client/active_record/gcm/app_spec.rb
281
292
  - spec/unit/client/active_record/gcm/notification_spec.rb
282
293
  - spec/unit/client/active_record/notification_spec.rb
294
+ - spec/unit/client/active_record/wns/badge_notification_spec.rb
295
+ - spec/unit/client/active_record/wns/raw_notification_spec.rb
283
296
  - spec/unit/client/active_record/wpns/app_spec.rb
284
297
  - spec/unit/client/active_record/wpns/notification_spec.rb
285
298
  - spec/unit/configuration_spec.rb
@@ -306,6 +319,7 @@ files:
306
319
  - spec/unit/daemon/store/redis_spec.rb
307
320
  - spec/unit/daemon/tcp_connection_spec.rb
308
321
  - spec/unit/daemon/wns/delivery_spec.rb
322
+ - spec/unit/daemon/wns/post_request_spec.rb
309
323
  - spec/unit/daemon/wpns/delivery_spec.rb
310
324
  - spec/unit/daemon_spec.rb
311
325
  - spec/unit/deprecatable_spec.rb
@@ -375,6 +389,8 @@ test_files:
375
389
  - spec/unit/client/active_record/gcm/app_spec.rb
376
390
  - spec/unit/client/active_record/gcm/notification_spec.rb
377
391
  - spec/unit/client/active_record/notification_spec.rb
392
+ - spec/unit/client/active_record/wns/badge_notification_spec.rb
393
+ - spec/unit/client/active_record/wns/raw_notification_spec.rb
378
394
  - spec/unit/client/active_record/wpns/app_spec.rb
379
395
  - spec/unit/client/active_record/wpns/notification_spec.rb
380
396
  - spec/unit/configuration_spec.rb
@@ -401,6 +417,7 @@ test_files:
401
417
  - spec/unit/daemon/store/redis_spec.rb
402
418
  - spec/unit/daemon/tcp_connection_spec.rb
403
419
  - spec/unit/daemon/wns/delivery_spec.rb
420
+ - spec/unit/daemon/wns/post_request_spec.rb
404
421
  - spec/unit/daemon/wpns/delivery_spec.rb
405
422
  - spec/unit/daemon_spec.rb
406
423
  - spec/unit/deprecatable_spec.rb