diff --git a/generators/templates/apn_migrations/009_create_pull_notifications.rb b/generators/templates/apn_migrations/009_create_pull_notifications.rb new file mode 100644 index 00000000..a4cc4ac3 --- /dev/null +++ b/generators/templates/apn_migrations/009_create_pull_notifications.rb @@ -0,0 +1,16 @@ +class CreatePullNotifications < ActiveRecord::Migration + def self.up + create_table :apn_pull_notifications do |t| + t.integer :app_id + t.string :title + t.string :content + t.string :link + + t.timestamps + end + end + + def self.down + drop_table :apn_pull_notifications + end +end diff --git a/lib/apn_on_rails/app/models/apn/app.rb b/lib/apn_on_rails/app/models/apn/app.rb index f45d3cec..1a996eef 100644 --- a/lib/apn_on_rails/app/models/apn/app.rb +++ b/lib/apn_on_rails/app/models/apn/app.rb @@ -49,6 +49,7 @@ def send_group_notifications unless self.unsent_group_notifications.nil? || self.unsent_group_notifications.empty? APN::Connection.open_for_delivery({:cert => self.cert}) do |conn, sock| unsent_group_notifications.each do |gnoty| + puts "number of devices is #{gnoty.devices.size}" gnoty.devices.each do |device| conn.write(gnoty.message_for_sending(device)) end diff --git a/lib/apn_on_rails/app/models/apn/group_notification.rb b/lib/apn_on_rails/app/models/apn/group_notification.rb index be4590fc..fb7f8e8f 100644 --- a/lib/apn_on_rails/app/models/apn/group_notification.rb +++ b/lib/apn_on_rails/app/models/apn/group_notification.rb @@ -27,18 +27,18 @@ def alert=(message) # Creates a Hash that will be the payload of an APN. # # Example: - # apn = APN::Notification.new + # apn = APN::GroupNotification.new # apn.badge = 5 # apn.sound = 'my_sound.aiff' # apn.alert = 'Hello!' # apn.apple_hash # => {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"}} # # Example 2: - # apn = APN::Notification.new + # apn = APN::GroupNotification.new # apn.badge = 0 # apn.sound = true # apn.custom_properties = {"typ" => 1} - # apn.apple_hast # => {"aps" => {"badge" => 0}} + # apn.apple_hash # => {"aps" => {"badge" => 0, "sound" => 1.aiff},"typ" => "1"} def apple_hash result = {} result['aps'] = {} diff --git a/lib/apn_on_rails/app/models/apn/notification.rb b/lib/apn_on_rails/app/models/apn/notification.rb index 04ebfd9d..068f13a8 100644 --- a/lib/apn_on_rails/app/models/apn/notification.rb +++ b/lib/apn_on_rails/app/models/apn/notification.rb @@ -47,7 +47,7 @@ def alert=(message) # apn.badge = 0 # apn.sound = true # apn.custom_properties = {"typ" => 1} - # apn.apple_hast # => {"aps" => {"badge" => 0}} + # apn.apple_hash # => {"aps" => {"badge" => 0, "sound" => "1.aiff"}, "typ" => "1"} def apple_hash result = {} result['aps'] = {} @@ -87,7 +87,7 @@ def message_for_sending def self.send_notifications ActiveSupport::Deprecation.warn("The method APN::Notification.send_notifications is deprecated. Use APN::App.send_notifications instead.") - APN::App.send_notfications + APN::App.send_notifications end end # APN::Notification \ No newline at end of file diff --git a/lib/apn_on_rails/app/models/apn/pull_notification.rb b/lib/apn_on_rails/app/models/apn/pull_notification.rb new file mode 100644 index 00000000..8216dfd7 --- /dev/null +++ b/lib/apn_on_rails/app/models/apn/pull_notification.rb @@ -0,0 +1,15 @@ +class APN::PullNotification < APN::Base + belongs_to :app, :class_name => 'APN::App' + + validates_presence_of :app_id + + def self.latest_since(app_id, since_date=nil) + conditions = if since_date + ["app_id = ? AND created_at > ?", app_id, since_date] + else + ["app_id = ?", app_id] + end + + first(:order => "created_at DESC", :conditions => conditions) + end +end \ No newline at end of file diff --git a/spec/apn_on_rails/app/models/apn/app_spec.rb b/spec/apn_on_rails/app/models/apn/app_spec.rb index 30b644ee..1a06460b 100644 --- a/spec/apn_on_rails/app/models/apn/app_spec.rb +++ b/spec/apn_on_rails/app/models/apn/app_spec.rb @@ -39,11 +39,10 @@ device = DeviceFactory.create({:app_id => app.id}) group = GroupFactory.create({:app_id => app.id}) device_grouping = DeviceGroupingFactory.create({:group_id => group.id,:device_id => device.id}) - puts "device grouping group is #{device_grouping.group.id} device grouping device is #{device_grouping.device.id}" - gnotys = [GroupNotificationFactory.create({:group_id => group}), - GroupNotificationFactory.create({:group_id => group})] + gnotys = [GroupNotificationFactory.create({:group_id => group.id}), + GroupNotificationFactory.create({:group_id => group.id})] gnotys.each_with_index do |gnoty, i| - gnoty.stub(:message_for_sending).at_least(:once).with(device).and_return("message-#{i}") + gnoty.stub!(:message_for_sending).and_return("message-#{i}") gnoty.should_receive(:sent_at=).with(instance_of(Time)) gnoty.should_receive(:save) end @@ -63,6 +62,29 @@ end + describe 'send single group notification' do + + it 'should send the argument group notification' do + app = AppFactory.create + device = DeviceFactory.create({:app_id => app.id}) + group = GroupFactory.create({:app_id => app.id}) + device_grouping = DeviceGroupingFactory.create({:group_id => group.id,:device_id => device.id}) + gnoty = GroupNotificationFactory.create({:group_id => group.id}) + gnoty.stub!(:message_for_sending).and_return("message-0") + gnoty.should_receive(:sent_at=).with(instance_of(Time)) + gnoty.should_receive(:save) + + app.should_receive(:cert).at_least(:once).and_return(app.apn_dev_cert) + + ssl_mock = mock('ssl_mock') + ssl_mock.should_receive(:write).with('message-0') + APN::Connection.should_receive(:open_for_delivery).and_yield(ssl_mock, nil) + + app.send_group_notification(gnoty) + end + + end + describe 'nil cert when sending notifications' do it 'should raise an exception for sending notifications for an app with no cert' do @@ -76,6 +98,35 @@ end + describe 'nil cert when sending group notifications' do + + it 'should raise an exception for sending group notifications for an app with no cert' do + app = AppFactory.create + APN::App.should_receive(:all).and_return([app]) + app.should_receive(:cert).and_return(nil) + lambda { + APN::App.send_group_notifications + }.should raise_error(APN::Errors::MissingCertificateError) + end + + end + + describe 'nil cert when sending single group notification' do + + it 'should raise an exception for sending group notifications for an app with no cert' do + app = AppFactory.create + device = DeviceFactory.create({:app_id => app.id}) + group = GroupFactory.create({:app_id => app.id}) + device_grouping = DeviceGroupingFactory.create({:group_id => group.id,:device_id => device.id}) + gnoty = GroupNotificationFactory.create({:group_id => group.id}) + app.should_receive(:cert).and_return(nil) + lambda { + app.send_group_notification(gnoty) + }.should raise_error(APN::Errors::MissingCertificateError) + end + + end + describe 'process_devices' do it 'should destroy devices that have a last_registered_at date that is before the feedback_at date' do diff --git a/spec/apn_on_rails/app/models/apn/group_notification_spec.rb b/spec/apn_on_rails/app/models/apn/group_notification_spec.rb new file mode 100644 index 00000000..2ce1544b --- /dev/null +++ b/spec/apn_on_rails/app/models/apn/group_notification_spec.rb @@ -0,0 +1,66 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb') + +describe APN::GroupNotification do + + describe 'alert' do + + it 'should trim the message to 150 characters' do + noty = APN::GroupNotification.new + noty.alert = 'a' * 200 + noty.alert.should == ('a' * 147) + '...' + end + + end + + describe 'apple_hash' do + + it 'should return a hash of the appropriate params for Apple' do + noty = APN::GroupNotification.first + noty.apple_hash.should == {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"},"typ" => "1"} + noty.custom_properties = nil + noty.apple_hash.should == {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"}} + noty.badge = nil + noty.apple_hash.should == {"aps" => {"sound" => "my_sound.aiff", "alert" => "Hello!"}} + noty.alert = nil + noty.apple_hash.should == {"aps" => {"sound" => "my_sound.aiff"}} + noty.sound = nil + noty.apple_hash.should == {"aps" => {}} + noty.sound = true + noty.apple_hash.should == {"aps" => {"sound" => "1.aiff"}} + end + + end + + describe 'to_apple_json' do + + it 'should return the necessary JSON for Apple' do + noty = APN::GroupNotification.first + noty.to_apple_json.should == %{{"typ":"1","aps":{"badge":5,"sound":"my_sound.aiff","alert":"Hello!"}}} + end + + end + + describe 'message_for_sending' do + + it 'should create a binary message to be sent to Apple' do + noty = APN::GroupNotification.first + noty.custom_properties = nil + device = DeviceFactory.new(:token => '5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz') + noty.message_for_sending(device).should == fixture_value('message_for_sending.bin') + end + + it 'should raise an APN::Errors::ExceededMessageSizeError if the message is too big' do + app = AppFactory.create + device = DeviceFactory.create({:app_id => app.id}) + group = GroupFactory.create({:app_id => app.id}) + device_grouping = DeviceGroupingFactory.create({:group_id => group.id,:device_id => device.id}) + noty = GroupNotificationFactory.new(:group_id => group.id, :sound => true, :badge => nil) + noty.send(:write_attribute, 'alert', 'a' * 183) + lambda { + noty.message_for_sending(device) + }.should raise_error(APN::Errors::ExceededMessageSizeError) + end + + end + +end \ No newline at end of file diff --git a/spec/apn_on_rails/app/models/apn/notification_spec.rb b/spec/apn_on_rails/app/models/apn/notification_spec.rb index acd66069..b86d0109 100644 --- a/spec/apn_on_rails/app/models/apn/notification_spec.rb +++ b/spec/apn_on_rails/app/models/apn/notification_spec.rb @@ -16,6 +16,8 @@ it 'should return a hash of the appropriate params for Apple' do noty = APN::Notification.first + noty.apple_hash.should == {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"},"typ" => "1"} + noty.custom_properties = nil noty.apple_hash.should == {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"}} noty.badge = nil noty.apple_hash.should == {"aps" => {"sound" => "my_sound.aiff", "alert" => "Hello!"}} @@ -33,7 +35,7 @@ it 'should return the necessary JSON for Apple' do noty = APN::Notification.first - noty.to_apple_json.should == %{{"aps":{"badge":5,"sound":"my_sound.aiff","alert":"Hello!"}}} + noty.to_apple_json.should == %{{"typ":"1","aps":{"badge":5,"sound":"my_sound.aiff","alert":"Hello!"}}} end end @@ -42,6 +44,7 @@ it 'should create a binary message to be sent to Apple' do noty = APN::Notification.first + noty.custom_properties = nil noty.device = DeviceFactory.new(:token => '5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz') noty.message_for_sending.should == fixture_value('message_for_sending.bin') end @@ -56,4 +59,13 @@ end + describe 'send_notifications' do + + it 'should warn the user the method is deprecated and call the corresponding method on APN::App' do + ActiveSupport::Deprecation.should_receive(:warn) + APN::App.should_receive(:send_notifications) + APN::Notification.send_notifications + end + end + end \ No newline at end of file diff --git a/spec/apn_on_rails/app/models/apn/pull_notification_spec.rb b/spec/apn_on_rails/app/models/apn/pull_notification_spec.rb new file mode 100644 index 00000000..8138c468 --- /dev/null +++ b/spec/apn_on_rails/app/models/apn/pull_notification_spec.rb @@ -0,0 +1,37 @@ +require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper.rb') + +describe APN::PullNotification do + + describe 'latest_since_when_already_seen_latest' do + + it 'should return nothing because since date is after the latest pull notification' do + app = APN::App.first + noty1 = PullNotificationFactory.create({:app_id => app.id}) + noty1.created_at = Time.now - 1.week + noty1.save + APN::PullNotification.latest_since(app.id,Time.now).should == nil + end + + end + + describe 'latest_since_when_have_not_seen_latest' do + + it 'should return the most recent pull notification because it has not yet been seen' do + app = APN::App.first + noty1 = PullNotificationFactory.create({:app_id => app.id}) + noty1.created_at = Time.now + 1.week + noty1.save + APN::PullNotification.latest_since(app.id,Time.now - 1.week).should == noty1 + end + + end + + describe 'latest_since_with_no_date' do + it 'should return the most recent pull notification because no date is given' do + app = APN::App.first + noty1 = APN::PullNotification.find(:first, :order => "created_at DESC") + APN::PullNotification.latest_since(app.id).should == noty1 + end + end + +end \ No newline at end of file diff --git a/spec/factories/group_factory.rb b/spec/factories/group_factory.rb index 47c8b0f1..a842a08f 100644 --- a/spec/factories/group_factory.rb +++ b/spec/factories/group_factory.rb @@ -3,7 +3,7 @@ module GroupFactory class << self def new(options = {}) - app = AppFactory.create + app = APN::App.first options = {:app_id => app.id, :name => GroupFactory.random_name}.merge(options) return APN::Group.new(options) end diff --git a/spec/factories/group_notification_factory.rb b/spec/factories/group_notification_factory.rb index fb03d076..6f921da8 100644 --- a/spec/factories/group_notification_factory.rb +++ b/spec/factories/group_notification_factory.rb @@ -5,7 +5,7 @@ class << self def new(options = {}) group = APN::Group.first options = {:group_id => group.id, :sound => 'my_sound.aiff', - :badge => 5, :alert => 'Hello!'}.merge(options) + :badge => 5, :alert => 'Hello!', :custom_properties => {'typ' => 1}}.merge(options) return APN::GroupNotification.new(options) end diff --git a/spec/factories/notification_factory.rb b/spec/factories/notification_factory.rb index 4c1cf74c..c695d283 100644 --- a/spec/factories/notification_factory.rb +++ b/spec/factories/notification_factory.rb @@ -5,7 +5,7 @@ class << self def new(options = {}) device = APN::Device.first options = {:device_id => device.id, :sound => 'my_sound.aiff', - :badge => 5, :alert => 'Hello!'}.merge(options) + :badge => 5, :alert => 'Hello!', :custom_properties => {'typ' => 1}}.merge(options) return APN::Notification.new(options) end diff --git a/spec/factories/pull_notification_factory.rb b/spec/factories/pull_notification_factory.rb new file mode 100644 index 00000000..0beac135 --- /dev/null +++ b/spec/factories/pull_notification_factory.rb @@ -0,0 +1,22 @@ +module PullNotificationFactory + + class << self + + def new(options = {}) + app = APN::App.first + options = {:app_id => app.id, :title => 'Pull Notification Title', + :content => 'blah blah blah', :link => 'http://www.prx.org'}.merge(options) + return APN::PullNotification.new(options) + end + + def create(options = {}) + noty = PullNotificationFactory.new(options) + noty.save + return noty + end + + end + +end + +PullNotificationFactory.create \ No newline at end of file