Skip to content

Commit

Permalink
Merge branch 'main' into iOS-Delivery-Method-updates
Browse files Browse the repository at this point in the history
  • Loading branch information
phil-6 authored Jan 23, 2024
2 parents 5f7eca8 + 40f6b70 commit d5a411b
Show file tree
Hide file tree
Showing 10 changed files with 114 additions and 60 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
### Unreleased

* Validate param key exists, not the value

### 2.0.3

* Notifier generator now ensures the `Notifier` suffix.
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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).

33 changes: 17 additions & 16 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion app/models/concerns/noticed/deliverable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
99 changes: 70 additions & 29 deletions docs/delivery_methods/fcm.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 3 additions & 0 deletions lib/noticed.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion lib/noticed/delivery_method.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module Noticed
class DeliveryMethod < ApplicationJob
class DeliveryMethod < Noticed.parent_class.constantize
include ApiClient
include RequiredOptions

Expand Down
11 changes: 10 additions & 1 deletion lib/noticed/delivery_methods/fcm.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions test/delivery_methods/action_cable_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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}
)
Expand Down
9 changes: 0 additions & 9 deletions test/dummy/app/channels/notification_channel.rb

This file was deleted.

0 comments on commit d5a411b

Please sign in to comment.