Skip to content

Commit

Permalink
Merge pull request #2 from jesseplusplus/features/expo-push
Browse files Browse the repository at this point in the history
Allow push notifications via Expo
  • Loading branch information
jesseplusplus authored Jul 19, 2021
2 parents 9063029 + db9cf81 commit a1738d4
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 12 deletions.
3 changes: 2 additions & 1 deletion app/controllers/api/v1/push/subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ def create
endpoint: subscription_params[:endpoint],
key_p256dh: subscription_params[:keys][:p256dh],
key_auth: subscription_params[:keys][:auth],
expo: subscription_params[:expo],
data: data_params,
user_id: current_user.id,
access_token_id: doorkeeper_token.id
Expand Down Expand Up @@ -46,7 +47,7 @@ def check_push_subscription
end

def subscription_params
params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh])
params.require(:subscription).permit(:endpoint, :expo, keys: [:auth, :p256dh])
end

def data_params
Expand Down
9 changes: 5 additions & 4 deletions app/models/web/push_subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@
#
# id :bigint(8) not null, primary key
# endpoint :string not null
# key_p256dh :string not null
# key_auth :string not null
# key_p256dh :string
# key_auth :string
# data :json
# created_at :datetime not null
# updated_at :datetime not null
# access_token_id :bigint(8)
# user_id :bigint(8)
# expo :string
#

class Web::PushSubscription < ApplicationRecord
Expand All @@ -21,8 +22,8 @@ class Web::PushSubscription < ApplicationRecord
has_one :session_activation, foreign_key: 'web_push_subscription_id', inverse_of: :web_push_subscription

validates :endpoint, presence: true
validates :key_p256dh, presence: true
validates :key_auth, presence: true
validates :key_p256dh, presence: true, unless: :expo?
validates :key_auth, presence: true, unless: :expo?

delegate :locale, to: :associated_user

Expand Down
38 changes: 36 additions & 2 deletions app/workers/web/push_notification_worker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@ def perform(subscription_id, notification_id)
# in the meantime, so we have to double-check before proceeding
return unless @notification.activity.present? && @subscription.pushable?(@notification)

payload = @subscription.encrypt(push_notification_json)
return expo_send if @subscription.expo?

payload = @subscription.encrypt(push_notification_json)

request_pool.with(@subscription.audience) do |http_client|
request = Request.new(:post, @subscription.endpoint, body: payload.fetch(:ciphertext), http_client: http_client)

Expand All @@ -38,7 +40,7 @@ def perform(subscription_id, notification_id)
# and must be removed

if (400..499).cover?(response.code) && ![408, 429].include?(response.code)
@subscription.destroy!
raise Mastodon::UnexpectedResponseError, response
elsif !(200...300).cover?(response.code)
raise Mastodon::UnexpectedResponseError, response
end
Expand All @@ -50,6 +52,29 @@ def perform(subscription_id, notification_id)

private

def expo_send
request_pool.with(@subscription.audience) do |http_client|
body = push_notification_json

request = Request.new(:post, @subscription.endpoint, body: body, http_client: http_client)

request.add_headers(
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Accept-Encoding' => 'gzip, deflate',
'Host' => 'exp.host',
'Ttl' => TTL,
'Urgency' => URGENCY,
)

request.perform do |response|
if !(200...300).cover?(response.code)
raise Mastodon::UnexpectedResponseError, response
end
end
end
end

def push_notification_json
json = I18n.with_locale(@subscription.locale || I18n.default_locale) do
ActiveModelSerializers::SerializableResource.new(
Expand All @@ -60,6 +85,15 @@ def push_notification_json
).as_json
end

if (@subscription.expo?)
json.delete :access_token
json.delete :preferred_locale
json.delete :notification_id
json.delete :notification_type

json[:to] = @subscription.expo
end

Oj.dump(json)
end

Expand Down
20 changes: 20 additions & 0 deletions db/migrate/20210708211904_add_expo_to_web_push_subscriptions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
class AddExpoToWebPushSubscriptions < ActiveRecord::Migration[6.1]
def up
safety_assured do
change_table(:web_push_subscriptions) do |t|
t.column :expo, :string
t.change :key_p256dh, :string, null: true
t.change :key_auth, :string, null: true
end
end
end
def down
safety_assured do
change_table(:web_push_subscriptions) do |t|
t.remove :expo
t.change :key_p256dh, :string, null: false
t.change :key_auth, :string, null: false
end
end
end
end
7 changes: 4 additions & 3 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2021_06_09_202149) do
ActiveRecord::Schema.define(version: 2021_07_08_211904) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -937,13 +937,14 @@

create_table "web_push_subscriptions", force: :cascade do |t|
t.string "endpoint", null: false
t.string "key_p256dh", null: false
t.string "key_auth", null: false
t.string "key_p256dh"
t.string "key_auth"
t.json "data"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.bigint "access_token_id"
t.bigint "user_id"
t.string "expo"
t.index ["access_token_id"], name: "index_web_push_subscriptions_on_access_token_id"
t.index ["user_id"], name: "index_web_push_subscriptions_on_user_id"
end
Expand Down
2 changes: 2 additions & 0 deletions spec/controllers/api/v1/push/subscriptions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
p256dh: 'BEm_a0bdPDhf0SOsrnB2-ategf1hHoCnpXgQsFj5JCkcoMrMt2WHoPfEYOYPzOIs9mZE8ZUaD7VA5vouy0kEkr8=',
auth: 'eH_C8rq2raXqlcBVDa1gLg==',
},
expo: 'token1234'
}
}.with_indifferent_access
end
Expand Down Expand Up @@ -53,6 +54,7 @@
expect(push_subscription.endpoint).to eq(create_payload[:subscription][:endpoint])
expect(push_subscription.key_p256dh).to eq(create_payload[:subscription][:keys][:p256dh])
expect(push_subscription.key_auth).to eq(create_payload[:subscription][:keys][:auth])
expect(push_subscription.expo).to eq(create_payload[:subscription][:expo])
expect(push_subscription.user_id).to eq user.id
expect(push_subscription.access_token_id).to eq token.id
end
Expand Down
19 changes: 17 additions & 2 deletions spec/workers/web/push_notification_worker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
allow(JWT).to receive(:encode).and_return('jwt.encoded.payload')

stub_request(:post, endpoint).to_return(status: 201, body: '')

subject.perform(subscription.id, notification.id)
end

it 'calls the relevant service with the correct headers' do
subject.perform(subscription.id, notification.id)

expect(a_request(:post, endpoint).with(headers: {
'Content-Encoding' => 'aesgcm',
'Content-Type' => 'application/octet-stream',
Expand All @@ -44,5 +44,20 @@
'Authorization' => 'WebPush jwt.encoded.payload',
}, body: "+\xB8\xDBT}\u0013\xB6\xDD.\xF9\xB0\xA7\xC8Ҁ\xFD\x99#\xF7\xAC\x83\xA4\xDB,\u001F\xB5\xB9w\x85>\xF7\xADr")).to have_been_made
end

it 'calls the relevant service with the correct headers and body when it is an expo subscription' do
allow_any_instance_of(subscription.class).to receive(:expo?).and_return(true)
allow_any_instance_of(subscription.class).to receive(:expo).and_return('ExpoToken1234')
subject.perform(subscription.id, notification.id)

expect(a_request(:post, endpoint).with(headers: {
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Accept-Encoding' => 'gzip, deflate',
'Host' => 'exp.host',
'Ttl' => '172800',
'Urgency' => 'normal',
}, body: { to: 'ExpoToken1234', title: be_an_instance_of(String), body: be_an_instance_of(String), icon: be_an_instance_of(String)})).to have_been_made
end
end
end

0 comments on commit a1738d4

Please sign in to comment.