rpush 2.6.0 → 2.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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