add push communication channel type
fixes CNVS-5794 links to an access token to get the proper ARN test plan: * set up an SNS app in AWS * configure your credentials in sns.yml * set sns_arn on a developer key to be the ARN of the app in SNS * using an access token created from that developer key, you should be able to create a push channel * you should see that channel in your profile (named after your developer key) Change-Id: I183241d02715252bf558c495d72d4995cea4232d Reviewed-on: https://gerrit.instructure.com/25281 Reviewed-by: Cody Cutrer <cody@instructure.com> Product-Review: Cody Cutrer <cody@instructure.com> QA-Review: Cody Cutrer <cody@instructure.com> Tested-by: Jenkins <jenkins@instructure.com>
This commit is contained in:
parent
ae09f28353
commit
4e7e22b852
2
Gemfile
2
Gemfile
|
@ -23,7 +23,7 @@ else
|
|||
gem 'authlogic', '3.2.0'
|
||||
end
|
||||
|
||||
gem "aws-sdk", '1.8.3.1'
|
||||
gem "aws-sdk", '1.21.0'
|
||||
gem 'barby', '0.5.0'
|
||||
gem 'bcrypt-ruby', '3.0.1'
|
||||
gem 'builder', '3.0.0'
|
||||
|
|
|
@ -83,9 +83,15 @@ class CommunicationChannelsController < ApplicationController
|
|||
# @argument communication_channel[address] [String]
|
||||
# An email address or SMS number.
|
||||
#
|
||||
# @argument communication_channel[type] [String, "email"|"sms"]
|
||||
# @argument communication_channel[type] [String, "email"|"sms"|"push"]
|
||||
# The type of communication channel.
|
||||
#
|
||||
# In order to enable push notification support, the server must be
|
||||
# properly configured (via sns.yml) to communicate with Amazon
|
||||
# Simple Notification Services, and the developer key used to create
|
||||
# the access token from this request must have an SNS ARN configured on
|
||||
# it.
|
||||
#
|
||||
# @argument skip_confirmation [Optional, Boolean]
|
||||
# Only valid for site admins making requests; If true, the channel is
|
||||
# automatically validated and no confirmation email or SMS is sent.
|
||||
|
@ -122,13 +128,24 @@ class CommunicationChannelsController < ApplicationController
|
|||
end
|
||||
end
|
||||
|
||||
if params[:communication_channel][:type] == CommunicationChannel::TYPE_PUSH
|
||||
if !@access_token
|
||||
return render :json => { errors: { type: 'Push is only supported when using an access token'}}, status: :bad_request
|
||||
end
|
||||
if !@access_token.developer_key.try(:sns_arn)
|
||||
return render :json => { errors: { type: 'SNS is not configured for this developer key'}}, status: :bad_request
|
||||
end
|
||||
skip_confirmation = true
|
||||
@cc = @user.communication_channels.create_push(@access_token, params[:communication_channel][:address])
|
||||
end
|
||||
|
||||
# Find or create the communication channel.
|
||||
@cc = @user.communication_channels.by_path(params[:communication_channel][:address]).
|
||||
@cc ||= @user.communication_channels.by_path(params[:communication_channel][:address]).
|
||||
find_by_path_type(params[:communication_channel][:type])
|
||||
@cc ||= @user.communication_channels.build(:path => params[:communication_channel][:address],
|
||||
:path_type => params[:communication_channel][:type])
|
||||
|
||||
if (!@cc.new_record? && !@cc.retired?)
|
||||
if (!@cc.new_record? && !@cc.retired? && @cc.path_type != CommunicationChannel::TYPE_PUSH)
|
||||
@cc.errors.add(:path, 'unique!')
|
||||
return render :json => @cc.errors.as_json, :status => :bad_request
|
||||
end
|
||||
|
|
|
@ -7,6 +7,8 @@ class AccessToken < ActiveRecord::Base
|
|||
serialize :scopes, Array
|
||||
validate :must_only_include_valid_scopes
|
||||
|
||||
has_many :communication_channels, dependent: :destroy
|
||||
|
||||
# For user-generated tokens, purpose can be manually set.
|
||||
# For app-generated tokens, this should be generated based
|
||||
# on the scope defined in the auth process (scope has not
|
||||
|
|
|
@ -30,12 +30,14 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
belongs_to :user
|
||||
has_many :notification_policies, :dependent => :destroy
|
||||
has_many :messages
|
||||
belongs_to :access_token
|
||||
|
||||
before_save :consider_retiring, :assert_path_type, :set_confirmation_code
|
||||
before_save :consider_building_pseudonym
|
||||
validates_presence_of :path, :path_type, :user, :workflow_state
|
||||
validate :uniqueness_of_path
|
||||
validate :not_otp_communication_channel, :if => lambda { |cc| cc.path_type == TYPE_SMS && cc.retired? && !cc.new_record? }
|
||||
validates_presence_of :access_token_id, if: lambda { |cc| cc.path_type == TYPE_PUSH }
|
||||
|
||||
acts_as_list :scope => :user_id
|
||||
|
||||
|
@ -50,6 +52,7 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
TYPE_CHAT = 'chat'
|
||||
TYPE_TWITTER = 'twitter'
|
||||
TYPE_FACEBOOK = 'facebook'
|
||||
TYPE_PUSH = 'push'
|
||||
|
||||
RETIRE_THRESHOLD = 5
|
||||
|
||||
|
@ -162,6 +165,8 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
res = self.user.user_services.for_service(TYPE_TWITTER).first.service_user_name rescue nil
|
||||
res ||= t :default_twitter_handle, 'Twitter Handle'
|
||||
res
|
||||
elsif self.path_type == TYPE_PUSH
|
||||
access_token.purpose ? "#{access_token.purpose} (#{access_token.developer_key.name})" : access_token.developer_key.name
|
||||
else
|
||||
self.path
|
||||
end
|
||||
|
@ -326,7 +331,7 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
# This is setup as a default in the database, but this overcomes misspellings.
|
||||
def assert_path_type
|
||||
pt = self.path_type
|
||||
self.path_type = TYPE_EMAIL unless pt == TYPE_EMAIL or pt == TYPE_SMS or pt == TYPE_CHAT or pt == TYPE_FACEBOOK or pt == TYPE_TWITTER
|
||||
self.path_type = TYPE_EMAIL unless pt == TYPE_EMAIL or pt == TYPE_SMS or pt == TYPE_CHAT or pt == TYPE_FACEBOOK or pt == TYPE_TWITTER or pt == TYPE_PUSH
|
||||
true
|
||||
end
|
||||
protected :assert_path_type
|
||||
|
@ -356,4 +361,28 @@ class CommunicationChannel < ActiveRecord::Base
|
|||
def has_merge_candidates?
|
||||
!merge_candidates(true).empty?
|
||||
end
|
||||
|
||||
def self.create_push(access_token, device_token)
|
||||
(scope(:find, :shard) || Shard.current).activate do
|
||||
connection.transaction do
|
||||
cc = new
|
||||
cc.path_type = CommunicationChannel::TYPE_PUSH
|
||||
cc.path = device_token
|
||||
cc.access_token = access_token
|
||||
cc.workflow_state = 'active'
|
||||
|
||||
# save first, so we can put the global id in it
|
||||
cc.save!
|
||||
response = DeveloperKey.sns.client.create_platform_endpoint(
|
||||
platform_application_arn: access_token.developer_key.sns_arn,
|
||||
token: device_token,
|
||||
custom_user_data: cc.global_id.to_s
|
||||
)
|
||||
|
||||
cc.internal_path = response.data[:endpoint_arn]
|
||||
cc.save!
|
||||
cc
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -82,4 +82,14 @@ class DeveloperKey < ActiveRecord::Base
|
|||
rescue URI::InvalidURIError
|
||||
return false
|
||||
end
|
||||
|
||||
# for now, only one AWS account for SNS is supported
|
||||
def self.sns
|
||||
if !defined?(@sns)
|
||||
settings = Setting.from_config('sns')
|
||||
@sns = nil
|
||||
@sns = AWS::SNS.new(settings) if settings
|
||||
end
|
||||
@sns
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
class AddPushColumns < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :developer_keys, :sns_arn, :string
|
||||
add_column :communication_channels, :access_token_id, :integer, limit: 8
|
||||
add_column :communication_channels, :internal_path, :string
|
||||
add_foreign_key :communication_channels, :access_tokens
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :developer_keys, :sns_arn
|
||||
remove_column :communication_channels, :access_token_id
|
||||
remove_column :communication_channels, :internal_path
|
||||
end
|
||||
end
|
|
@ -151,6 +151,34 @@ describe 'CommunicationChannels API', :type => :integration do
|
|||
|
||||
response.code.should eql '401'
|
||||
end
|
||||
|
||||
context 'push' do
|
||||
before { @post_params.merge!(communication_channel: {address: 'myphone', type: 'push'}) }
|
||||
|
||||
it 'should complain about sns not being configured' do
|
||||
raw_api_call(:post, @path, @path_options, @post_params)
|
||||
|
||||
response.code.should eql '400'
|
||||
end
|
||||
|
||||
it "should work" do
|
||||
client = mock()
|
||||
sns = mock()
|
||||
sns.stubs(:client).returns(client)
|
||||
DeveloperKey.stubs(:sns).returns(sns)
|
||||
dk = DeveloperKey.default
|
||||
dk.sns_arn = 'apparn'
|
||||
dk.save!
|
||||
$spec_api_tokens[@user] = @user.access_tokens.create!(developer_key: dk).full_token
|
||||
response = mock()
|
||||
response.expects(:data).returns(endpoint_arn: 'endpointarn')
|
||||
client.expects(:create_platform_endpoint).once.returns(response)
|
||||
|
||||
json = api_call(:post, @path, @path_options, @post_params)
|
||||
json['type'].should == 'push'
|
||||
json['workflow_state'].should == 'active'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in New Issue