diff --git a/CHANGELOG.md b/CHANGELOG.md index ade56a63..7005c0d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ### Unreleased +* Validate param key exists, not the value + ### 2.0.3 * Notifier generator now ensures the `Notifier` suffix. diff --git a/README.md b/README.md index 9651b137..0096f328 100644 --- a/README.md +++ b/README.md @@ -587,6 +587,14 @@ end # @post.noticed_events.each { |ne| ne.notifications... } ``` +#### ActiveJob Parent Class + +Noticed uses its own `Noticed::ApplicationJob` as the base job for all notifications. In the event that you would like to customize the parent job class, there is a `parent_class` attribute that can be overridden with your own class. This should be done in a `noticed.rb` initializer. + +```ruby +Noticed.parent_class = "ApplicationJob" +``` + #### Handling Deleted Records Generally we recommend using a `dependent: ___` relationship on your models to avoid cases where Noticed Events or Notifications are left lingering when your models are destroyed. In the case that they are or data becomes mis-matched, you’ll likely run into deserialization issues. That may be globally alleviated with the following snippet, but use with caution. @@ -611,4 +619,3 @@ DATABASE_URL=postgres://127.0.0.1/noticed_test rails test ## 📝 License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). - diff --git a/UPGRADE.md b/UPGRADE.md index 7684369d..8442582c 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -290,40 +290,41 @@ Options for delivery methods have been renamed for clarity and consistency. #### ActionCable -The `format` option has been renamed to `message`. -The `Noticed::NotificationChannel` has been removed and an example channel is provided in the [ActionCable docs](docs/delivery_methods/action_cable.md). +- The `format` option has been renamed to `message`. +- The `Noticed::NotificationChannel` has been removed and an example channel is provided in the [ActionCable docs](docs/delivery_methods/action_cable.md). #### Email Delivery Method -`method` is now a required option. Previously, it was inferred from the notification name but we've decided it would be better to be explicit. +- `method` is now a required option. Previously, it was inferred from the notification name but we've decided it would be better to be explicit. #### FCM -The `format` option has been renamed to `json`. -The `device_tokens` option is now required and should return an Array of device tokens. -The `invalid_token` option replaces the `cleanup_device_tokens` method for handling invalid/expired tokens. +- The `format` option has been renamed to `json`. +- The `device_tokens` option is now required and should return an Array of device tokens. +- The `invalid_token` option replaces the `cleanup_device_tokens` method for handling invalid/expired tokens. +- We no longer wrap the json payload in the `message{}` key. This means we are more compatible with the FCM docs and any future changes that Google make. #### iOS -The `cert_path` option has been renamed to `apns_key` and should be given the key and not a path. -The `device_tokens` option is now required and should return an Array of device tokens. -The `invalid_token` option replaces the `cleanup_device_tokens` method for handling invalid/expired tokens. +- The `cert_path` option has been renamed to `apns_key` and should be given the key and not a path. +- The `device_tokens` option is now required and should return an Array of device tokens. +- The `invalid_token` option replaces the `cleanup_device_tokens` method for handling invalid/expired tokens. #### Microsoft Teams -The `format` option has been renamed to `json`. +- The `format` option has been renamed to `json`. #### Slack -The `format` option has been renamed to `json`. -The `url` option now defaults to `"https://slack.com/api/chat.postMessage` instead of `Rails.application.credentials.dig(:slack, :notification_url)` +- The `format` option has been renamed to `json`. +- The `url` option now defaults to `"https://slack.com/api/chat.postMessage` instead of `Rails.application.credentials.dig(:slack, :notification_url)` #### Twilio Messaging -Twilio has been renamed to `:twilio_messaging` to make room for `:twilio_voice` and other services they may provide in the future. -The `format` option has been renamed to `json`. +- Twilio has been renamed to `:twilio_messaging` to make room for `:twilio_voice` and other services they may provide in the future. +- The `format` option has been renamed to `json`. #### Vonage SMS -Vonage has been renamed to `:vonage_sms` to make room for other Vonage services in the future. -The `format` option has been renamed to `json` and is now required. +- Vonage has been renamed to `:vonage_sms` to make room for other Vonage services in the future. +- The `format` option has been renamed to `json` and is now required. diff --git a/app/models/concerns/noticed/deliverable.rb b/app/models/concerns/noticed/deliverable.rb index e0a385ba..05621c07 100644 --- a/app/models/concerns/noticed/deliverable.rb +++ b/app/models/concerns/noticed/deliverable.rb @@ -103,7 +103,7 @@ def validate! def validate_params! required_param_names.each do |param_name| - raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params[param_name].present? + raise ValidationError, "Param `#{param_name}` is required for #{self.class.name}." unless params.has_key?(param_name) end end diff --git a/docs/delivery_methods/fcm.md b/docs/delivery_methods/fcm.md index 759a1bdd..09aa5af5 100644 --- a/docs/delivery_methods/fcm.md +++ b/docs/delivery_methods/fcm.md @@ -26,51 +26,92 @@ See the below instructions on where to store this information within your applic class CommentNotification deliver_by :fcm do |config| config.credentials = Rails.root.join("config/certs/fcm.json") + config.device_tokens = ->(recipient) { recipient.notification_tokens.where(platform: "fcm").pluck(:token) } config.json = ->(device_token) { { - token: device_token, - notification: { - title: "Test Title", - body: "Test body" + message: { + token: device_token, + notification: { + title: "Test Title", + body: "Test body" + } } } } + config.if = ->(recipient) { recipient.android_notifications? } end end ``` ## Options -* `json` - Customize the Firebase Cloud Messaging notification object. This can be a Lambda or Symbol of a method name on the notifier. The callable object will be given the device token as an argument. +### `json` +Customize the Firebase Cloud Messaging notification object. This can be a Lambda or Symbol of a method name on the notifier. + +The callable object will be given the device token as an argument. -* `credentials` +There are lots of options of now to structure a FCM notification message. See https://firebase.google.com/docs/cloud-messaging/concept-options for more details. + +### `credentials` The location of your Firebase Cloud Messaging credentials. -- When a String object: `deliver_by :fcm, credentials: "config/credentials/fcm.json"` - * Interally, this string is passed to `Rails.root.join()` as an argument so there is no need to do this beforehand. -- When a Pathname object: `deliver_by :fcm, credentials: Rails.root.join("config/credentials/fcm.json")` - * The Pathname object can point to any location where you are storing your credentials. -- When a Hash object: `deliver_by :fcm, credentials: credentials_hash` - ```ruby - credentials_hash = { - "type": "service_account", - "project_id": "test-project-1234", - "private_key_id": ..., - etc..... - } - ``` - * A Hash which contains your credentials -- When a Symbol: `deliver_by :fcm, credentials: :fcm_credentials, format: :format_notification` - ```ruby - def fcm_credentials - Rails.root.join("config/certs/fcm.json") - end - ``` +#### When a String Object + +Internally, this string is passed to `Rails.root.join()` as an argument so there is no need to do this beforehand. + +```ruby +deliver_by :fcm do |config| + config.credentials = "config/credentials/fcm.json" +end +``` + +#### When a Pathname object + +The Pathname object can point to any location where you are storing your credentials. + +```ruby +deliver_by :fcm do |config| + config.credentials = Rails.root.join("config/credentials/fcm.json") +end +``` + +#### When a Hash object + +A Hash which contains your credentials + +```ruby +deliver_by :fcm do |config| + config.credentials = credentials_hash +end + +credentials_hash = { + "type": "service_account", + "project_id": "test-project-1234", + "private_key_id": ..., + etc..... +} +``` + +#### When a Symbol + +Points to a method which can return a Hash of your credentials, Pathname, or String to your credentials like the examples above. + +We pass the notification object as an argument to the method. If you don't need to use it you can use the splat operator `(*)` to ignore it. + +```ruby +deliver_by :fcm do |config| + config.credentials = :fcm_credentials + config.json = :format_notification +end + +def fcm_credentials(*) + Rails.root.join("config/certs/fcm.json") +end +``` - * Points to a method which can return a Hash of your credentials, Pathname, or String to your credentials like the examples above. +#### Otherwise -Otherwise, if the credentials option is left out, it will look for your credentials in: `Rails.application.credentials.fcm` +If the credentials option is left out, it will look for your credentials in: `Rails.application.credentials.fcm` ## Gathering Notification Tokens diff --git a/lib/noticed.rb b/lib/noticed.rb index c39d548d..444a34c2 100644 --- a/lib/noticed.rb +++ b/lib/noticed.rb @@ -39,6 +39,9 @@ module DeliveryMethods autoload :Webhook, "noticed/delivery_methods/webhook" end + mattr_accessor :parent_class + @@parent_class = "Noticed::ApplicationJob" + class ValidationError < StandardError end diff --git a/lib/noticed/delivery_method.rb b/lib/noticed/delivery_method.rb index f84dc63e..5e7b7069 100644 --- a/lib/noticed/delivery_method.rb +++ b/lib/noticed/delivery_method.rb @@ -1,5 +1,5 @@ module Noticed - class DeliveryMethod < ApplicationJob + class DeliveryMethod < Noticed.parent_class.constantize include ApiClient include RequiredOptions diff --git a/lib/noticed/delivery_methods/fcm.rb b/lib/noticed/delivery_methods/fcm.rb index ad166637..99ad60c1 100644 --- a/lib/noticed/delivery_methods/fcm.rb +++ b/lib/noticed/delivery_methods/fcm.rb @@ -14,7 +14,7 @@ def deliver def send_notification(device_token) post_request("https://fcm.googleapis.com/v1/projects/#{credentials[:project_id]}/messages:send", headers: {authorization: "Bearer #{access_token}"}, - json: notification.instance_exec(device_token, &config[:json])) + json: format_notification(device_token)) rescue Noticed::ResponseUnsuccessful => exception if exception.response.code == "404" && config[:invalid_token] notification.instance_exec(device_token, &config[:invalid_token]) @@ -23,6 +23,15 @@ def send_notification(device_token) end end + def format_notification(device_token) + method = config[:json] + if method.is_a?(Symbol) && event.respond_to?(method) + event.send(method, device_token) + else + notification.instance_exec(device_token, &method) + end + end + def credentials @credentials ||= begin value = evaluate_option(:credentials) diff --git a/test/delivery_methods/action_cable_test.rb b/test/delivery_methods/action_cable_test.rb index 38c8b163..94551265 100644 --- a/test/delivery_methods/action_cable_test.rb +++ b/test/delivery_methods/action_cable_test.rb @@ -9,10 +9,10 @@ class ActionCableDeliveryMethodTest < ActiveSupport::TestCase test "sends websocket message" do user = users(:one) - channel = NotificationChannel.broadcasting_for(user) + channel = Noticed::NotificationChannel.broadcasting_for(user) set_config( - channel: "NotificationChannel", + channel: "Noticed::NotificationChannel", stream: user, message: {foo: :bar} ) diff --git a/test/dummy/app/channels/notification_channel.rb b/test/dummy/app/channels/notification_channel.rb deleted file mode 100644 index 66cdb454..00000000 --- a/test/dummy/app/channels/notification_channel.rb +++ /dev/null @@ -1,9 +0,0 @@ -class NotificationChannel < ApplicationCable::Channel - def subscribed - stream_for current_user - end - - def unsubscribed - stop_all_streams - end -end