├── .codeclimate.yml ├── .gitignore ├── .rspec ├── .rubocop.yml ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── generators │ ├── templates │ │ └── add_device_token.rb │ └── wor │ │ └── push │ │ └── notifications │ │ └── aws │ │ └── install_generator.rb └── wor │ └── push │ └── notifications │ ├── aws.rb │ └── aws │ ├── android_push_json_builder.rb │ ├── exceptions.rb │ ├── ios_push_json_builder.rb │ ├── push_notifications.rb │ ├── validators │ └── push_notifications_validator.rb │ └── version.rb ├── spec ├── spec_helper.rb └── wor │ ├── generators │ └── install_generator_spec.rb │ └── push │ └── notifications │ ├── aws_spec.rb │ ├── mocks │ ├── sns_client_mock.rb │ ├── user_with_device_tokens_attribute.rb │ └── user_without_device_tokens_attribute.rb │ ├── push_notifications_spec.rb │ └── validators │ └── push_notifications_validator_spec.rb └── wor-push-notifications-aws.gemspec /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | rubocop: 3 | enabled: true 4 | channel: rubocop-0-48 5 | 6 | ratings: 7 | paths: 8 | - "lib/**/*" 9 | - "spec/**/*" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | .byebug_history 11 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | # This is the configuration used to check the rubocop source code. 2 | 3 | Rails: 4 | Enabled: true 5 | 6 | AllCops: 7 | Exclude: 8 | - wor-push-notifications-aws.gemspec 9 | - lib/generators/templates/add_device_token.rb 10 | 11 | Documentation: 12 | Enabled: false 13 | 14 | LineLength: 15 | Max: 99 16 | 17 | Style/FrozenStringLiteralComment: 18 | Enabled: false 19 | 20 | Style/BlockDelimiters: 21 | IgnoredMethods: ['have_structure'] 22 | 23 | Metrics/BlockLength: 24 | ExcludedMethods: ['describe', 'context', 'before'] 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2.2 4 | - 2.3.3 5 | - 2.4.0 6 | 7 | install: 8 | - gem install bundler 9 | - bundle install --retry=3 10 | 11 | script: 12 | - bundle exec rubocop -R --format simple 13 | - bundle exec rspec 14 | - bundle exec codeclimate-test-reporter 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change log 2 | 3 | [0.1.2] - 2017-08-04 4 | 5 | - Adds validations for the `message_content` 6 | - Accepts `message_content` with Strings or Symbols as keys 7 | 8 | [0.1.1] 9 | 10 | - Fixes SNS::Client location 11 | 12 | [0.1.0] 13 | 14 | - wor-push-notifications-aws gem first release. 15 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in wor-push-notifications-aws.gemspec 4 | gemspec 5 | 6 | group :development do 7 | gem 'bundler', '~> 1.13' 8 | gem 'byebug', '~> 9.0' 9 | gem 'codeclimate-test-reporter', '~> 1.0.0' 10 | gem 'generator_spec' 11 | gem 'rake', '~> 10.0' 12 | gem 'rspec', '~> 3.0' 13 | gem 'rspec-rails', '~> 3.5' 14 | gem 'rubocop', '~> 0.48' 15 | gem 'simplecov', '~> 0.13' 16 | end 17 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Wolox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wor::Push::Notifications::Aws 2 | 3 | [![Gem Version](https://badge.fury.io/rb/wor-push-notifications-aws.svg)](https://badge.fury.io/rb/wor-push-notifications-aws) 4 | [![Dependency Status](https://gemnasium.com/badges/github.com/Wolox/wor-push-notifications-aws.svg)](https://gemnasium.com/github.com/Wolox/wor-push-notifications-aws) 5 | [![Build Status](https://travis-ci.org/Wolox/wor-push-notifications-aws.svg)](https://travis-ci.org/Wolox/wor-push-notifications-aws) 6 | [![Code Climate](https://codeclimate.com/github/Wolox/wor-push-notifications-aws/badges/gpa.svg)](https://codeclimate.com/github/Wolox/wor-push-notifications-aws) 7 | [![Test Coverage](https://codeclimate.com/github/Wolox/wor-push-notifications-aws/badges/coverage.svg)](https://codeclimate.com/github/Wolox/wor-push-notifications-aws/coverage) 8 | 9 | Provide basic setup for storing device tokens and sending Push Notifications to your application using AWS Simple Notification Service (SNS). 10 | 11 | ## Installation 12 | 13 | Add this line to your application's Gemfile: 14 | 15 | ```ruby 16 | gem 'wor-push-notifications-aws' 17 | ``` 18 | 19 | And then execute: 20 | 21 | $ bundle 22 | 23 | Or install it yourself as: 24 | 25 | $ gem install wor-push-notifications-aws 26 | 27 | ## Configuration 28 | To use the gem, firstly we have to configure it. But don’t worry since the configuration simply consists of these two steps: 29 | 1. Firstly, under the config/initializers dir, create the file `wor_push_notifications_aws.rb`: 30 | ```ruby 31 | Wor::Push::Notifications::Aws.configure do |config| 32 | config.device_types = [:ios, :android] # optional 33 | config.table_name = 'users' # optional 34 | config.aws_region = 'us-east-1' 35 | config.aws_android_arn = 'some:android:arn' # mandatory field if you choose to use Android devices 36 | config.aws_ios_arn = 'some:ios:arn' # mandatory field if you choose to use iOS devices 37 | config.aws_ios_sandbox = true # mandatory field if you choose to use iOS devices 38 | end 39 | ``` 40 | If you don't know where to get the arn values, please see [SNS Setup](#sns-setup) section. 41 | 42 | 2. The following step involves running the install generator, which basically creates a migration file to add a column to your selected table in order to store the tokens. To run the generator, run the following commands: 43 | ```ruby 44 | $ rails generate wor:push:notifications:aws:install 45 | $ rake db:migrate 46 | ``` 47 | 48 | ## Usage 49 | ***Note***: If you haven’t configured sns, now it’s the moment. (See [SNS Setup](#sns-setup)). 50 | 51 | So far we have the gem and sns configured, so let’s move to what the gem can do. 52 | As it’s purpose is to make the app send push notifications, there are 3 methods, **add_token**, **delete_token** and **send_message**, to add/delete the device_token, and to send the message. 53 | 54 | The device token is a unique token obtained from each pair of App and Device. This token is used for creating an endpoint to identify your users' phones to which you will be sending the push notifications. 55 | 56 | The device type identifies from which type of device you have obtained the device token, because the data used for each platform is different. 57 | iOS and Android are the only device types supported for the moment. 58 | 59 | ### Add token 60 | Attach device_tokens to a given user instance: 61 | ```ruby 62 | Wor::Push::Notifications::Aws.add_token(user, device_token, device_type) 63 | ``` 64 | #### Parameters 65 | - user: Instance where we want to store the device_token, to which we will send push notifications. 66 | - device_token: Unique identifier you get from the the app. 67 | - device_type: So far we support the values :android or :ios 68 | 69 | ### Delete token 70 | Delete token from the user instance: 71 | ```ruby 72 | Wor::Push::Notifications::Aws.delete_token(user, device_token) 73 | ``` 74 | #### Parameters 75 | - user: Instance where we want to store the device_token, to which we will send push notifications. 76 | - device_token: Unique identifier you get from the the app. 77 | 78 | ### Send message 79 | Send a given message to the user instance: 80 | ```ruby 81 | Wor::Push::Notifications::Aws.send_message(user, message_content) 82 | ``` 83 | #### Parameters 84 | - user: Instance which will receive the message. It must have the `device_tokens` from the user's phones. 85 | - message_content: Message you want to send to the user. This parameter must have a JSON format. 86 | - It **requires** the `message` field. 87 | - You can add a `badge` field (integer type) to be included in the app icon to show how many pending notifications the user has. 88 | - You can include any other field in the JSON, with the information you need to send in the push notification 89 | 90 | **\*BADGE:** iOS shows the badge automatically, but you have to include it yourself in Android devices. 91 | 92 | ***message_content example:*** 93 | 94 | Suppose you have a billing application, you will send the data related to the new bill that has been charged in the system and the amount of unread notifications. 95 | 96 | ``` 97 | message_content = { message: 'You have a new bill!', badge: 5, account_id: 93, bill_id: 45 } 98 | ``` 99 | 100 | ## AWS Credentials 101 | In order to use Aws SNS, you'll need to have Aws configured with the right credentials. 102 | You can set up them according to one of the ways explained under the 103 | "credentials" section at http://docs.aws.amazon.com/sdkforruby/api/Aws/SNS/Client.html. 104 | We recommend to use ENV variables with Rails secrets when setting up the configuration. 105 | 106 | *Note:* Aws region is specified in the [initializer file](#configuration). 107 | 108 | ## SNS Setup 109 | 110 | **If you HAVE a SNS Application follow this instructions** 111 | 112 | - i) Log in the AWS Console 113 | - ii) Click on Services and select Simple Notification Services in Messaging group 114 | - iii) Select Applications tab in the left panel 115 | - iv) You will have your applications' **ARN** listed in a table 116 | - v) When selecting any of them you can see more details of each application, with the information if it's a SANDBOX environment in the iOS case 117 | 118 | You will have to create an instance profile for the Elastic Beanstalk environment that's running your application, with the permissions to access the Simple Notification Service or get an AWS_ACCESS_KEY and AWS_SECRET_KEY pair to access this service from outside of AWS. 119 | 120 | If you want more information about AWS SNS visit the [documentation](http://docs.aws.amazon.com/sns/latest/dg/SNSMobilePush.html) page. 121 | If you want more information on how to use AWS with Google Cloud Messaging (for Android) check this [documentation](http://docs.aws.amazon.com/sns/latest/dg/mobile-push-gcm.html). 122 | If you want more information on how to use AWS with Apple Push Notifications Service (for iOS) check this [documentation](http://docs.aws.amazon.com/sns/latest/dg/mobile-push-apns.html). 123 | 124 | ## Requirements 125 | Since a json attribute is needed to store device_tokens on the user table, 126 | Postgres 9.3 or higher is required. 127 | 128 | ## Contributing 129 | 1. Fork it 130 | 2. Create your feature branch (`git checkout -b my-new-feature`) 131 | 3. Commit your changes (`git commit -am 'Add some feature'`) 132 | 4. Run rubocop lint (`bundle exec rubocop -R --format simple`) 133 | 5. Run rspec tests (`bundle exec rspec`) 134 | 6. Push your branch (`git push origin my-new-feature`) 135 | 7. Create a new Pull Request 136 | 137 | ## About 138 | This project is maintained by [Leandro Masello](https://github.com/lmasello) along with 139 | [Francisco Landino](https://github.com/plandino) and it was written by 140 | [Wolox](http://www.wolox.com.ar). 141 | ![Wolox](https://raw.githubusercontent.com/Wolox/press-kit/master/logos/logo_banner.png) 142 | 143 | ## License 144 | **wor-push-notifications-aws** is available under the MIT [license](https://raw.githubusercontent.com/Wolox/wor-push-notifications-aws/master/LICENSE.txt). 145 | Copyright (c) 2017 Wolox 146 | 147 | Permission is hereby granted, free of charge, to any person obtaining a copy 148 | of this software and associated documentation files (the "Software"), to deal 149 | in the Software without restriction, including without limitation the rights 150 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 151 | copies of the Software, and to permit persons to whom the Software is 152 | furnished to do so, subject to the following conditions: 153 | 154 | The above copyright notice and this permission notice shall be included in 155 | all copies or substantial portions of the Software. 156 | 157 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 158 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 159 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 160 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 161 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 162 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 163 | THE SOFTWARE. 164 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rspec/core/rake_task' 3 | 4 | RSpec::Core::RakeTask.new(:spec) 5 | 6 | task default: :spec 7 | -------------------------------------------------------------------------------- /lib/generators/templates/add_device_token.rb: -------------------------------------------------------------------------------- 1 | class AddDeviceTokenTo<%= table_name.to_s.camelize %> < ActiveRecord::Migration<%= migration_version %> 2 | def change 3 | add_column :<%= table_name %>, :device_tokens, :json, default: {} 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/generators/wor/push/notifications/aws/install_generator.rb: -------------------------------------------------------------------------------- 1 | require 'rails' 2 | require 'rails/generators' 3 | require 'rails/generators/migration' 4 | 5 | module Wor 6 | module Push 7 | module Notifications 8 | module Aws 9 | module Generators 10 | class InstallGenerator < Rails::Generators::Base 11 | include Rails::Generators::Migration 12 | source_root File.expand_path('./../../../../../templates', __FILE__) 13 | 14 | desc "This generator creates the migration file required to add the attribute 15 | device_tokens to the given table" 16 | 17 | def copy_migration 18 | file_name = 'add_device_token' 19 | migration_name = "#{file_name}_to_#{table_name}.rb" 20 | if self.class.migration_exists?('db/migrate', migration_name) 21 | say_status('skipped', "Migration #{migration_name} already exists") 22 | else 23 | migration_template "#{file_name}.rb", "db/migrate/#{migration_name}", 24 | migration_version: migration_version, table_name: table_name 25 | end 26 | end 27 | 28 | # Implement the required interface for Rails::Generators::Migration 29 | def self.next_migration_number(_path) 30 | Time.now.utc.strftime('%Y%m%d%H%M%S') 31 | end 32 | 33 | private 34 | 35 | def rails5? 36 | Rails.version.start_with? '5' 37 | end 38 | 39 | def migration_version 40 | "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if rails5? 41 | end 42 | 43 | def table_name 44 | Wor::Push::Notifications::Aws.table_name 45 | end 46 | end 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws.rb: -------------------------------------------------------------------------------- 1 | require 'wor/push/notifications/aws/version' 2 | require 'wor/push/notifications/aws/push_notifications' 3 | 4 | module Wor 5 | module Push 6 | module Notifications 7 | module Aws 8 | DEVICE_TYPES = %i[ios android].freeze 9 | 10 | @config = { 11 | device_types: DEVICE_TYPES, 12 | table_name: :users 13 | } 14 | 15 | def self.configure 16 | yield self 17 | end 18 | 19 | # Precondition: types must be an array of symbols containing valid device types. 20 | # Every type included in this array must be a valid device type 21 | # (you can ask for valid device types with the method valid_device_types). 22 | # Default is ['ios' 'android']. 23 | def self.device_types=(types) 24 | raise ArgumentError, 'Argument must be an array of symbols' unless types.is_a?(Array) 25 | types.each do |type| 26 | raise ArgumentError, "Invalid type #{type}" unless DEVICE_TYPES.include?(type) 27 | end 28 | @config[:device_types] = types 29 | end 30 | 31 | # Precondition: table_name must be a string which points out the name of the table that 32 | # will store the device_tokens. 33 | # Default is 'users'. 34 | def self.table_name=(table_name) 35 | raise ArgumentError, 'Argument must be a string' unless table_name.is_a?(String) 36 | raise ArgumentError, 'Argument must not be an empty string' if table_name.empty? 37 | @config[:table_name] = table_name.pluralize.to_sym 38 | end 39 | 40 | def self.aws_ios_arn=(aws_ios_arn) 41 | raise ArgumentError, 'Argument must be a string' unless aws_ios_arn.is_a?(String) 42 | @config[:aws_ios_arn] = aws_ios_arn 43 | end 44 | 45 | def self.aws_ios_sandbox=(aws_ios_sandbox) 46 | raise ArgumentError, 'Argument must be a boolean' unless boolean?(aws_ios_sandbox) 47 | @config[:aws_ios_sandbox] = aws_ios_sandbox 48 | end 49 | 50 | def self.aws_android_arn=(aws_android_arn) 51 | raise ArgumentError, 'Argument must be a string' unless aws_android_arn.is_a?(String) 52 | @config[:aws_android_arn] = aws_android_arn 53 | end 54 | 55 | def self.aws_region=(aws_region) 56 | raise ArgumentError, 'Argument must be a string' unless aws_region.is_a?(String) 57 | @config[:aws_region] = aws_region 58 | end 59 | 60 | def self.device_types 61 | @config[:device_types] 62 | end 63 | 64 | def self.table_name 65 | @config[:table_name] 66 | end 67 | 68 | def self.aws_ios_arn 69 | @config[:aws_ios_arn] 70 | end 71 | 72 | def self.aws_ios_sandbox 73 | @config[:aws_ios_sandbox] 74 | end 75 | 76 | def self.aws_android_arn 77 | @config[:aws_android_arn] 78 | end 79 | 80 | def self.aws_region 81 | @config[:aws_region] 82 | end 83 | 84 | def self.config 85 | @config 86 | end 87 | 88 | def self.add_token(user, device_token, device_type) 89 | PushNotifications.add_token(user, device_token, device_type.to_sym) 90 | end 91 | 92 | def self.delete_token(user, device_token) 93 | PushNotifications.delete_token(user, device_token) 94 | end 95 | 96 | def self.send_message(user, message_content) 97 | PushNotifications.send_message(user, message_content) 98 | end 99 | 100 | def self.valid_device_types 101 | DEVICE_TYPES 102 | end 103 | 104 | def self.boolean?(value) 105 | value.is_a?(TrueClass) || value.is_a?(FalseClass) 106 | end 107 | end 108 | end 109 | end 110 | end 111 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws/android_push_json_builder.rb: -------------------------------------------------------------------------------- 1 | module Wor 2 | module Push 3 | module Notifications 4 | module Aws 5 | class AndroidPushJsonBuilder 6 | class << self 7 | def build_json(message_content) 8 | { GCM: { data: message_content }.to_json } 9 | end 10 | end 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws/exceptions.rb: -------------------------------------------------------------------------------- 1 | module Wor 2 | module Push 3 | module Notifications 4 | module Aws 5 | module Exceptions 6 | class ModelWithoutDeviceTokensAttribute < StandardError; end 7 | end 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws/ios_push_json_builder.rb: -------------------------------------------------------------------------------- 1 | module Wor 2 | module Push 3 | module Notifications 4 | module Aws 5 | class IosPushJsonBuilder 6 | class << self 7 | def build_json(message_content) 8 | unless Wor::Push::Notifications::Aws.aws_ios_sandbox 9 | return { APNS: aps_content(message_content) } 10 | end 11 | { APNS_SANDBOX: aps_content(message_content) } 12 | end 13 | 14 | private 15 | 16 | def aps_content(message_content) 17 | { aps: { alert: message_content[:message], badge: message_content[:badge], 18 | sound: 'default' } }.merge(message_content).to_json 19 | end 20 | end 21 | end 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws/push_notifications.rb: -------------------------------------------------------------------------------- 1 | require 'wor/push/notifications/aws/android_push_json_builder' 2 | require 'wor/push/notifications/aws/ios_push_json_builder' 3 | require 'wor/push/notifications/aws/validators/push_notifications_validator' 4 | require 'aws-sdk-rails' 5 | 6 | module Wor 7 | module Push 8 | module Notifications 9 | module Aws 10 | class PushNotifications 11 | class << self 12 | def add_token(user, device_token, device_type) 13 | PushNotificationsValidator.new(user, device_token, device_type).validate_add_token 14 | device_token = device_token.to_s.gsub(/\s+/, '') 15 | return true if user.device_tokens.key?(device_token) 16 | endpoint = sns.create_platform_endpoint( 17 | platform_application_arn: app_arn(device_type), token: device_token 18 | ) 19 | user.device_tokens[device_token] = { 'device_type' => device_type, 20 | 'endpoint_arn' => endpoint[:endpoint_arn] } 21 | user.save 22 | end 23 | 24 | def delete_token(user, device_token) 25 | PushNotificationsValidator.new(user).validate_delete_token 26 | device_token = device_token.to_s.gsub(/\s+/, '') 27 | return true unless user.device_tokens.key?(device_token) 28 | sns.delete_endpoint(endpoint_arn: user.device_tokens[device_token]['endpoint_arn']) 29 | user.device_tokens.delete(device_token) 30 | user.save 31 | end 32 | 33 | def send_message(user, message_content) 34 | PushNotificationsValidator.new(user).validate_model 35 | message_content = PushNotificationsValidator.validate_message_content( 36 | message_content 37 | ) 38 | send_notifications_to_user(user, message_content) 39 | end 40 | 41 | private 42 | 43 | def send_notifications_to_user(user, message_content) 44 | return false if user.device_tokens.values.empty? 45 | send_notifications_to_devices(user, message_content) 46 | true 47 | end 48 | 49 | def send_notifications_to_devices(user, message_content) 50 | user.device_tokens.each do |device_token, token| 51 | message = prepare_message(token['device_type'], message_content) 52 | begin 53 | sns.publish(target_arn: token['endpoint_arn'], message: message.to_json, 54 | message_structure: 'json') 55 | rescue 56 | user.device_tokens.delete(device_token) 57 | end 58 | end 59 | user.save if user.device_tokens_changed? 60 | end 61 | 62 | def prepare_message(device_type, message_content) 63 | app_arn_for_device[device_type.to_sym][:json_builder].build_json(message_content) 64 | end 65 | 66 | def sns 67 | @sns_client ||= ::Aws::SNS::Client.new( 68 | region: Wor::Push::Notifications::Aws.aws_region 69 | ) 70 | end 71 | 72 | def app_arn(device_type) 73 | app_arn_for_device[device_type.to_sym][:arn] 74 | end 75 | 76 | def app_arn_for_device 77 | { 78 | ios: { 79 | arn: Wor::Push::Notifications::Aws.aws_ios_arn, 80 | json_builder: IosPushJsonBuilder 81 | }, 82 | android: { 83 | arn: Wor::Push::Notifications::Aws.aws_android_arn, 84 | json_builder: AndroidPushJsonBuilder 85 | } 86 | } 87 | end 88 | end 89 | end 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws/validators/push_notifications_validator.rb: -------------------------------------------------------------------------------- 1 | module Wor 2 | module Push 3 | module Notifications 4 | module Aws 5 | class PushNotificationsValidator 6 | def initialize(model, device_token = nil, device_type = nil) 7 | @model = model 8 | @device_token = device_token 9 | @device_type = device_type 10 | end 11 | 12 | def validate_add_token 13 | validate_model_existance 14 | validate_existence_of_attributes_in_model 15 | validate_parameters 16 | end 17 | 18 | def validate_delete_token 19 | validate_model_existance 20 | validate_existence_of_attributes_in_model 21 | end 22 | 23 | def validate_model 24 | validate_model_existance 25 | validate_existence_of_attributes_in_model 26 | end 27 | 28 | class << self 29 | def validate_message_content(message_content) 30 | raise ArgumentError, message_content_type_error unless message_content.is_a?(Hash) 31 | message_content = message_content.with_indifferent_access 32 | raise ArgumentError, message_content_error if message_content[:message].blank? 33 | badge_check(message_content) 34 | end 35 | 36 | private 37 | 38 | def message_content_error 39 | "the message_content must have a 'message' field" 40 | end 41 | 42 | def message_content_type_error 43 | 'message_content must be a Hash' 44 | end 45 | 46 | def badge_check(message_content) 47 | message_content[:badge] ||= 1 48 | message_content 49 | end 50 | end 51 | 52 | private 53 | 54 | def validate_model_existance 55 | raise ArgumentError, message_for_nil_model if @model.nil? 56 | end 57 | 58 | def validate_existence_of_attributes_in_model 59 | return if @model.has_attribute?(:device_tokens) 60 | raise Wor::Push::Notifications::Aws::Exceptions::ModelWithoutDeviceTokensAttribute, 61 | message_for_missing_attribute_in_model 62 | end 63 | 64 | def validate_parameters 65 | raise ArgumentError, message_for_invalid_device_type unless device_type_valid? 66 | raise ArgumentError, message_for_nil_device_token if @device_token.blank? 67 | end 68 | 69 | def message_for_invalid_device_type 70 | "Invalid device_type. It has to be one of the types configured \ 71 | #{Wor::Push::Notifications::Aws.device_types}." 72 | end 73 | 74 | def message_for_missing_attribute_in_model 75 | 'Missing attribute device_tokens for model. Have you run the gem migration?' 76 | end 77 | 78 | def message_for_nil_device_token 79 | 'device_token cannot be nil.' 80 | end 81 | 82 | def message_for_nil_model 83 | 'your entity instance cannot be nil.' 84 | end 85 | 86 | def device_type_valid? 87 | Wor::Push::Notifications::Aws.device_types.include? @device_type 88 | end 89 | end 90 | end 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/wor/push/notifications/aws/version.rb: -------------------------------------------------------------------------------- 1 | module Wor 2 | module Push 3 | module Notifications 4 | module Aws 5 | VERSION = '0.1.2'.freeze 6 | end 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'simplecov' 2 | SimpleCov.start 3 | $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) 4 | require 'wor/push/notifications/aws' 5 | require 'wor/push/notifications/aws/exceptions' 6 | require 'rails' 7 | 8 | require 'byebug' 9 | -------------------------------------------------------------------------------- /spec/wor/generators/install_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'generator_spec' 2 | require 'generators/wor/push/notifications/aws/install_generator' 3 | 4 | describe Wor::Push::Notifications::Aws::Generators::InstallGenerator, type: :generator do 5 | context 'generating the initializer ' do 6 | destination File.expand_path('./../../../../tmp', __FILE__) 7 | 8 | before(:all) do 9 | prepare_destination 10 | run_generator 11 | end 12 | 13 | let(:migration_version) do 14 | "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]" if Rails.version.start_with? '5' 15 | end 16 | let(:current_migration_number) do 17 | Wor::Push::Notifications::Aws::Generators::InstallGenerator 18 | .current_migration_number("#{destination_root}/db/migrate/") 19 | end 20 | 21 | it 'generates the correct structure for initializer' do 22 | migration_file = "#{current_migration_number}_add_device_token_to_users.rb" 23 | first_line = "class AddDeviceTokenToUsers < ActiveRecord::Migration#{migration_version}" 24 | third_line = 'add_column :users, :device_tokens, :json, default: {}' 25 | expect(destination_root).to(have_structure { 26 | directory 'db' do 27 | directory 'migrate' do 28 | file migration_file do 29 | contains first_line 30 | contains third_line 31 | end 32 | end 33 | end 34 | }) 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/wor/push/notifications/aws_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Wor::Push::Notifications::Aws do 4 | it 'has a version number' do 5 | expect(Wor::Push::Notifications::Aws::VERSION).not_to be nil 6 | end 7 | 8 | describe '.config' do 9 | it 'has configurations' do 10 | expect(described_class.config).not_to be nil 11 | end 12 | 13 | it 'has a default table' do 14 | expect(described_class.table_name).not_to be nil 15 | end 16 | 17 | it 'has a default device types' do 18 | expect(described_class.device_types).not_to be nil 19 | end 20 | end 21 | 22 | describe '.configure' do 23 | context 'when configuring the table name' do 24 | let!(:default_table_name) { described_class.table_name.to_s } 25 | context 'with correct values' do 26 | context 'with table name pluralized' do 27 | let(:new_table_name) { 'clients' } 28 | 29 | before do 30 | described_class.configure do |config| 31 | config.table_name = new_table_name 32 | end 33 | end 34 | 35 | it 'can be configured' do 36 | expect(described_class.table_name).to eq(new_table_name.to_sym) 37 | end 38 | 39 | after do 40 | described_class.configure do |config| 41 | config.table_name = default_table_name 42 | end 43 | end 44 | end 45 | 46 | context 'with table name not pluralized' do 47 | let(:new_table_name) { 'client' } 48 | let(:new_table_name_pluralized) { 'client'.pluralize } 49 | 50 | before do 51 | described_class.configure do |config| 52 | config.table_name = new_table_name 53 | end 54 | end 55 | 56 | it 'can be configured' do 57 | expect(described_class.table_name).to eq(new_table_name_pluralized.to_sym) 58 | end 59 | 60 | after do 61 | described_class.configure do |config| 62 | config.table_name = default_table_name 63 | end 64 | end 65 | end 66 | end 67 | 68 | context 'with incorrect values' do 69 | let(:new_table_name) { 123 } 70 | let(:wrong_config) do 71 | described_class.configure do |config| 72 | config.table_name = new_table_name 73 | end 74 | end 75 | 76 | it 'raises ArgumentError' do 77 | expect { wrong_config }.to raise_error(ArgumentError, 'Argument must be a string') 78 | end 79 | end 80 | 81 | context 'with empty name' do 82 | let(:new_table_name) { '' } 83 | let(:wrong_config) do 84 | described_class.configure do |config| 85 | config.table_name = new_table_name 86 | end 87 | end 88 | 89 | it 'raises ArgumentError' do 90 | expect { wrong_config }.to raise_error(ArgumentError, 91 | 'Argument must not be an empty string') 92 | end 93 | end 94 | end 95 | 96 | context 'when configuring the device types' do 97 | let!(:default_device_types) { described_class.device_types } 98 | context 'with correct values' do 99 | let(:new_device_types) { [:android] } 100 | before do 101 | described_class.configure do |config| 102 | config.device_types = new_device_types 103 | end 104 | end 105 | 106 | it 'can be configured' do 107 | expect(described_class.device_types).to eq(new_device_types) 108 | end 109 | 110 | after do 111 | described_class.configure do |config| 112 | config.device_types = default_device_types 113 | end 114 | end 115 | end 116 | context 'with incorrect values' do 117 | context 'with incorrect argument (syntactically)' do 118 | let(:new_device_types) { :ios } 119 | let(:wrong_config) do 120 | described_class.configure do |config| 121 | config.device_types = new_device_types 122 | end 123 | end 124 | 125 | it 'raises ArgumentError' do 126 | expect { wrong_config }.to raise_error(ArgumentError, 127 | 'Argument must be an array of symbols') 128 | end 129 | end 130 | context 'with invalid type (semantically)' do 131 | let(:new_device_types) { %i[android ois] } 132 | let(:wrong_config) do 133 | described_class.configure do |config| 134 | config.device_types = new_device_types 135 | end 136 | end 137 | 138 | it 'raises ArgumentError' do 139 | expect { wrong_config }.to raise_error(ArgumentError, /Invalid type/) 140 | end 141 | end 142 | end 143 | end 144 | 145 | context 'when configuring aws region' do 146 | context 'with correct argument' do 147 | let(:aws_region) { 'us-west-1' } 148 | before do 149 | described_class.configure do |config| 150 | config.aws_region = aws_region 151 | end 152 | end 153 | 154 | it 'can be configured' do 155 | expect(described_class.aws_region).to eq(aws_region) 156 | end 157 | end 158 | 159 | context 'with incorrect argument' do 160 | let(:wrong_aws_region) { 1234 } 161 | let(:wrong_config) do 162 | described_class.configure do |config| 163 | config.aws_region = wrong_aws_region 164 | end 165 | end 166 | 167 | it 'raises ArgumentError' do 168 | expect { wrong_config }.to raise_error(ArgumentError, /Argument must be a string/) 169 | end 170 | end 171 | end 172 | 173 | context 'when configuring aws ios arn' do 174 | context 'with correct argument' do 175 | let(:aws_ios_arn) { 'arn:aws:app/APNS_SANDBOX' } 176 | before do 177 | described_class.configure do |config| 178 | config.aws_ios_arn = aws_ios_arn 179 | end 180 | end 181 | 182 | it 'can be configured' do 183 | expect(described_class.aws_ios_arn).to eq(aws_ios_arn) 184 | end 185 | end 186 | 187 | context 'with incorrect argument' do 188 | let(:wrong_arn) { 1234 } 189 | let(:wrong_config) do 190 | described_class.configure do |config| 191 | config.aws_ios_arn = wrong_arn 192 | end 193 | end 194 | 195 | it 'raises ArgumentError' do 196 | expect { wrong_config }.to raise_error(ArgumentError, /Argument must be a string/) 197 | end 198 | end 199 | end 200 | 201 | context 'when configuring aws ios sandbox' do 202 | context 'with correct argument' do 203 | let(:aws_ios_sandbox) { [true, false].sample } 204 | before do 205 | described_class.configure do |config| 206 | config.aws_ios_sandbox = aws_ios_sandbox 207 | end 208 | end 209 | 210 | it 'can be configured' do 211 | expect(described_class.aws_ios_sandbox).to eq(aws_ios_sandbox) 212 | end 213 | end 214 | 215 | context 'with incorrect argument' do 216 | let(:wrong_aws_ios_sandbox) { 'true' } 217 | let(:wrong_config) do 218 | described_class.configure do |config| 219 | config.aws_ios_sandbox = wrong_aws_ios_sandbox 220 | end 221 | end 222 | 223 | it 'raises ArgumentError' do 224 | expect { wrong_config }.to raise_error(ArgumentError, /Argument must be a boolean/) 225 | end 226 | end 227 | end 228 | 229 | context 'when configuring aws android arn' do 230 | context 'with correct argument' do 231 | let(:aws_android_arn) { 'arn:aws:app/GCM' } 232 | before do 233 | described_class.configure do |config| 234 | config.aws_android_arn = aws_android_arn 235 | end 236 | end 237 | 238 | it 'can be configured' do 239 | expect(described_class.aws_android_arn).to eq(aws_android_arn) 240 | end 241 | end 242 | 243 | context 'with incorrect argument' do 244 | let(:wrong_arn) { 1234 } 245 | let(:wrong_config) do 246 | described_class.configure do |config| 247 | config.aws_android_arn = wrong_arn 248 | end 249 | end 250 | 251 | it 'raises ArgumentError' do 252 | expect { wrong_config }.to raise_error(ArgumentError, /Argument must be a string/) 253 | end 254 | end 255 | end 256 | end 257 | end 258 | -------------------------------------------------------------------------------- /spec/wor/push/notifications/mocks/sns_client_mock.rb: -------------------------------------------------------------------------------- 1 | class SnsClientMock 2 | def create_platform_endpoint(params) 3 | { endpoint_arn: params[:token] } 4 | end 5 | 6 | def delete_endpoint(_endpoint_arn) 7 | true 8 | end 9 | 10 | def publish 11 | true 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/wor/push/notifications/mocks/user_with_device_tokens_attribute.rb: -------------------------------------------------------------------------------- 1 | class UserWithDeviceTokensAttribute 2 | attr_reader :device_tokens 3 | 4 | def initialize(email) 5 | @email = email 6 | @device_tokens = {} 7 | end 8 | 9 | def has_attribute?(_attr) 10 | true 11 | end 12 | 13 | def device_tokens_changed? 14 | false 15 | end 16 | 17 | def save 18 | true 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/wor/push/notifications/mocks/user_without_device_tokens_attribute.rb: -------------------------------------------------------------------------------- 1 | class UserWithoutDeviceTokensAttribute 2 | def initialize(email) 3 | @email = email 4 | end 5 | 6 | def has_attribute?(_attr) 7 | false 8 | end 9 | 10 | def save 11 | false 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/wor/push/notifications/push_notifications_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require_relative './mocks/user_with_device_tokens_attribute' 3 | require_relative './mocks/sns_client_mock' 4 | 5 | describe Wor::Push::Notifications::Aws::PushNotifications do 6 | before do 7 | allow(::Aws::SNS::Client).to receive(:new).and_return(SnsClientMock.new) 8 | end 9 | 10 | describe '#add_token' do 11 | subject(:add_token) { described_class.add_token(user, device_token, device_type) } 12 | 13 | let(:user) { UserWithDeviceTokensAttribute.new('user@example.com') } 14 | let(:device_type) { :android } 15 | let(:device_token) { 'app:arn:token' } 16 | 17 | it 'validates the method' do 18 | expect_any_instance_of(Wor::Push::Notifications::Aws::PushNotificationsValidator) 19 | .to receive(:validate_add_token) 20 | add_token 21 | end 22 | 23 | context 'when the user already contains the device_token to add' do 24 | before { user.device_tokens[device_token] = 'some_value' } 25 | 26 | it 'returns true' do 27 | expect(add_token).to be true 28 | end 29 | 30 | it 'does not modify the current value' do 31 | expect { add_token }.not_to(change { user.device_tokens }) 32 | end 33 | end 34 | 35 | context 'when the user does not contain the device_token' do 36 | it 'adds the token' do 37 | add_token 38 | expect(user.device_tokens[device_token].keys).to match_array %w[device_type endpoint_arn] 39 | end 40 | 41 | it 'returns true' do 42 | expect(add_token).to be true 43 | end 44 | end 45 | end 46 | 47 | describe '#delete_token' do 48 | subject(:delete_token) { described_class.delete_token(user, device_token) } 49 | let(:user) { UserWithDeviceTokensAttribute.new('user@example.com') } 50 | let(:device_token) { 'app:arn:token' } 51 | 52 | it 'validates the method' do 53 | expect_any_instance_of(Wor::Push::Notifications::Aws::PushNotificationsValidator) 54 | .to receive(:validate_delete_token) 55 | delete_token 56 | end 57 | 58 | context 'when the user does not have the device_token' do 59 | it 'does not modify the user' do 60 | expect { delete_token }.not_to(change { user }) 61 | end 62 | 63 | it 'returns true' do 64 | expect(delete_token).to be true 65 | end 66 | end 67 | 68 | context 'when the user has the device_token' do 69 | before { user.device_tokens[device_token] = 'some_value' } 70 | 71 | it 'Modifies the user\'s device_tokens attribute' do 72 | expect { delete_token }.to(change { user.device_tokens }) 73 | end 74 | 75 | it 'returns true' do 76 | expect(delete_token).to be true 77 | end 78 | end 79 | end 80 | 81 | describe '#send_message' do 82 | subject(:send_message) { described_class.send_message(user, message_content) } 83 | let(:user) { UserWithDeviceTokensAttribute.new('user@example.com') } 84 | let(:message_content) { { message: 'A message' } } 85 | 86 | it 'validates the method' do 87 | expect_any_instance_of(Wor::Push::Notifications::Aws::PushNotificationsValidator) 88 | .to receive(:validate_model) 89 | send_message 90 | end 91 | 92 | it 'validates the message_content' do 93 | expect(Wor::Push::Notifications::Aws::PushNotificationsValidator) 94 | .to receive(:validate_message_content) 95 | send_message 96 | end 97 | 98 | context 'when the user does not contain device token values' do 99 | it 'returns false' do 100 | expect(send_message).to be false 101 | end 102 | end 103 | 104 | context 'when the user contains device token values' do 105 | let(:device_token) { 'app:arn:token' } 106 | let(:device_type) { :android } 107 | 108 | before { described_class.add_token(user, device_token, device_type) } 109 | 110 | it 'calls the sns publish method' do 111 | expect_any_instance_of(SnsClientMock).to receive(:publish) 112 | send_message 113 | end 114 | 115 | it 'returns true' do 116 | expect(send_message).to be true 117 | end 118 | 119 | context 'when passing a message_content with symbols as keys' do 120 | let(:message_content) { { message: 'A message' } } 121 | 122 | it 'sends the message' do 123 | expect_any_instance_of(SnsClientMock).to receive(:publish) 124 | send_message 125 | end 126 | end 127 | 128 | context 'when passing a hash with strings as keys' do 129 | let(:message_content) { { 'message' => 'A message' } } 130 | 131 | it 'sends the message' do 132 | expect_any_instance_of(SnsClientMock).to receive(:publish) 133 | send_message 134 | end 135 | end 136 | end 137 | end 138 | end 139 | -------------------------------------------------------------------------------- /spec/wor/push/notifications/validators/push_notifications_validator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | require_relative './../mocks/user_with_device_tokens_attribute' 3 | require_relative './../mocks/user_without_device_tokens_attribute' 4 | 5 | describe Wor::Push::Notifications::Aws::PushNotificationsValidator do 6 | subject { described_class.new(model, device_token, device_type) } 7 | let(:user_mail) { 'example@example.com' } 8 | 9 | describe '#validate_add_token' do 10 | let(:validate_add_token) { subject.validate_add_token } 11 | 12 | context 'when passing right arguments' do 13 | let(:model) { UserWithDeviceTokensAttribute.new(user_mail) } 14 | let(:device_token) { '1234567890' } 15 | let(:device_type) { :ios } 16 | 17 | it 'does not raise error' do 18 | expect { validate_add_token }.not_to raise_error 19 | end 20 | end 21 | 22 | context 'when the model is nil' do 23 | let(:model) { nil } 24 | let(:device_token) { '1234567890' } 25 | let(:device_type) { :ios } 26 | 27 | it 'raises runtime error with a descriptive message' do 28 | expect { validate_add_token }.to raise_error(ArgumentError, 29 | /your entity instance cannot be nil./) 30 | end 31 | end 32 | 33 | context 'when the model does not have device_tokens attribute' do 34 | let(:model) { UserWithoutDeviceTokensAttribute.new(user_mail) } 35 | let(:device_token) { '1234567890' } 36 | let(:device_type) { :ios } 37 | 38 | it 'raises runtime error with a descriptive message' do 39 | expect { validate_add_token }.to raise_error( 40 | Wor::Push::Notifications::Aws::Exceptions::ModelWithoutDeviceTokensAttribute, 41 | /Missing attribute device_tokens/ 42 | ) 43 | end 44 | end 45 | 46 | context 'with invalid device_type' do 47 | let(:model) { UserWithDeviceTokensAttribute.new(user_mail) } 48 | let(:device_token) { '1234567890' } 49 | let(:device_type) { :iosss } 50 | 51 | it 'raises argument error with a descriptive message' do 52 | expect { validate_add_token }.to raise_error(ArgumentError, 53 | /Invalid device_type. It has to be one of /) 54 | end 55 | end 56 | 57 | context 'with nil device_token' do 58 | let(:model) { UserWithDeviceTokensAttribute.new(user_mail) } 59 | let(:device_token) { nil } 60 | let(:device_type) { :ios } 61 | 62 | it 'raises argument error with a descriptive message' do 63 | expect { validate_add_token }.to raise_error(ArgumentError, 64 | /device_token cannot be nil./) 65 | end 66 | end 67 | end 68 | 69 | describe '#validate_delete_token' do 70 | let(:validate_delete_token) { subject.validate_delete_token } 71 | let(:device_token) { nil } 72 | let(:device_type) { nil } 73 | 74 | context 'when the model is nil' do 75 | let(:model) { nil } 76 | 77 | it 'raises runtime error with a descriptive message' do 78 | expect { validate_delete_token }.to raise_error(ArgumentError, 79 | /your entity instance cannot be nil./) 80 | end 81 | end 82 | 83 | context 'when the model contains device_tokens attribute' do 84 | let(:model) { UserWithDeviceTokensAttribute.new(user_mail) } 85 | 86 | it 'does not raise error' do 87 | expect { validate_delete_token }.not_to raise_error 88 | end 89 | end 90 | 91 | context 'when the model does not have the device_tokens attribute' do 92 | let(:model) { UserWithoutDeviceTokensAttribute.new(user_mail) } 93 | 94 | it 'raises runtime error with a descriptive message' do 95 | expect { validate_delete_token }.to raise_error( 96 | Wor::Push::Notifications::Aws::Exceptions::ModelWithoutDeviceTokensAttribute, 97 | /Missing attribute device_tokens/ 98 | ) 99 | end 100 | end 101 | end 102 | 103 | describe '#validate_model' do 104 | let(:validate_model) { subject.validate_model } 105 | let(:device_token) { nil } 106 | let(:device_type) { nil } 107 | 108 | context 'with valid parameters' do 109 | let(:model) { UserWithDeviceTokensAttribute.new(user_mail) } 110 | 111 | it 'does not raise error' do 112 | expect { validate_model }.not_to raise_error 113 | end 114 | end 115 | 116 | context 'when the model is nil' do 117 | let(:model) { nil } 118 | 119 | it 'raises runtime error with a descriptive message' do 120 | expect { validate_model }.to raise_error(ArgumentError, 121 | /your entity instance cannot be nil./) 122 | end 123 | end 124 | 125 | context 'when the model does not have the device_tokens attribute' do 126 | let(:model) { UserWithoutDeviceTokensAttribute.new(user_mail) } 127 | 128 | it 'raises runtime error with a descriptive message' do 129 | expect { validate_model }.to raise_error( 130 | Wor::Push::Notifications::Aws::Exceptions::ModelWithoutDeviceTokensAttribute, 131 | /Missing attribute device_tokens/ 132 | ) 133 | end 134 | end 135 | end 136 | 137 | describe '.validate_message_content' do 138 | let(:validate_message_content) { described_class.validate_message_content(message_content) } 139 | 140 | context 'when passing a message_content without a badge field' do 141 | let(:message_content) { { message: 'A message' } } 142 | 143 | it 'adds the badge field' do 144 | expect(validate_message_content[:badge]).to eq 1 145 | end 146 | end 147 | 148 | context 'when passing a message_content with symbols as keys' do 149 | let(:message_content) { { message: 'A message' } } 150 | 151 | it 'does not raise error' do 152 | expect { validate_message_content }.not_to raise_error 153 | end 154 | end 155 | 156 | context 'when passing a message_content with strings as keys' do 157 | let(:message_content) { { 'message' => 'A message' } } 158 | 159 | it 'does not raise error' do 160 | expect { validate_message_content }.not_to raise_error 161 | end 162 | end 163 | 164 | context 'when passing a message_content that is not a Hash' do 165 | let(:message_content) { [5, 'String'].sample } 166 | 167 | it 'raises argument error with a descriptive message' do 168 | expect { validate_message_content } 169 | .to raise_error(ArgumentError, /message_content must be a Hash/) 170 | end 171 | end 172 | end 173 | end 174 | -------------------------------------------------------------------------------- /wor-push-notifications-aws.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'wor/push/notifications/aws/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "wor-push-notifications-aws" 8 | spec.version = Wor::Push::Notifications::Aws::VERSION 9 | spec.authors = ["Leandro Masello", "Francisco Landino"] 10 | spec.email = ["francisco.landino@wolox.com.ar", "leandro.masello@wolox.com.ar"] 11 | 12 | spec.summary = 'Easily send Push Notifications to your application using AWS Simple Notification Service (SNS)' 13 | spec.description = 'Provide basic set up for storing device tokens and sending Push Notifications to your application using AWS Simple Notification Service (SNS)' 14 | spec.homepage = "https://github.com/Wolox/wor-push-notifications-aws" 15 | spec.license = "MIT" 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 18 | f.match(%r{^(test|spec|features)/}) 19 | end 20 | spec.require_paths = ["lib"] 21 | 22 | spec.add_dependency 'railties', '>= 4.1.0', '< 5.2' 23 | spec.add_dependency 'aws-sdk-rails', "~> 1.0" 24 | end 25 | --------------------------------------------------------------------------------