add api endpoint to post global annoucement
***test plan 1. Make a POST like /accounts/1/account_notifications 2. Required Parameters: 1) account_notification[subject] 2) account_notification[start_at] 3) account_notification[end_at] 4) account_notification[message] 3. Optional Parameters: 1) account_notification[icon] 'warning' | 'information' | 'question' | 'error' | 'calendar' note: defaults to warning 2) account_notification_roles[] 'StudentEnrollment', 'TeacherEnrollment' etc... this defaults to all roles 4. Only account admins should be able to make the api call 5. Should not be able to create an account notification with an end_at < start_at closes: PS-1730 closes: PS-1872 Change-Id: Ide86722598ae4a7ab565422f2996015b48cf8910 Reviewed-on: https://gerrit.instructure.com/37477 Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Clare Strong <clare@instructure.com> Product-Review: Matt Fairbourn <mfairbourn@instructure.com> Reviewed-by: Brandon Broschinsky <brandonbr@instructure.com>
This commit is contained in:
parent
d7ed7a5911
commit
7e6519d586
|
@ -1,6 +1,117 @@
|
|||
#
|
||||
# Copyright (C) 2011 - 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# @API Account Notifications
|
||||
#
|
||||
# API for account notifications.
|
||||
# @model AccountNotificaion
|
||||
# {
|
||||
# "id": "AccountNotification",
|
||||
# "description": "",
|
||||
# "properties": {
|
||||
# "subject": {
|
||||
# "description": "The subject of the notifications",
|
||||
# "example": "Attention Students",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "message": {
|
||||
# "description": "The message to be sent in the notification.",
|
||||
# "example": "This is a test of the notification system.",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "start_at": {
|
||||
# "description": "When to send out the notification.",
|
||||
# "example": "2013-08-28T23:59:00-06:00",
|
||||
# "type": "datetime"
|
||||
# },
|
||||
# "end_at": {
|
||||
# "description": "When to expire the notification.",
|
||||
# "example": "2013-08-29T23:59:00-06:00",
|
||||
# "type": "datetime"
|
||||
# },
|
||||
# "icon": {
|
||||
# "description": "The icon to display with the message. Defaults to warning.",
|
||||
# "example": "[\"information\"]",
|
||||
# "type": "array",
|
||||
# "items": {"type": "string"},
|
||||
# "allowableValues": {
|
||||
# "values": [
|
||||
# "warning",
|
||||
# "information",
|
||||
# "question",
|
||||
# "error",
|
||||
# "calendar"
|
||||
# ]
|
||||
# }
|
||||
# },
|
||||
# "roles": {
|
||||
# "description": "The roles to send the notification to. If roles is not passed it defaults to all roles",
|
||||
# "example": "[\"StudentEnrollment\"]",
|
||||
# "type": "array",
|
||||
# "items": {"type": "string"}
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
class AccountNotificationsController < ApplicationController
|
||||
include Api::V1::AccountNotifications
|
||||
before_filter :require_account_admin
|
||||
|
||||
|
||||
# @API Create a global notification
|
||||
# Create and return a new global notification for an account.
|
||||
#
|
||||
# @argument account_notification[subject] [String]
|
||||
# The subject of the notification.
|
||||
#
|
||||
# @argument account_notification[message] [String]
|
||||
# The message body of the notification.
|
||||
#
|
||||
# @argument account_notification[start_at] [DateTime]
|
||||
# The start date and time of the notification in ISO8601 format.
|
||||
# e.g. 2014-01-01T01:00Z
|
||||
#
|
||||
# @argument account_notification[end_at] [DateTime]
|
||||
# The end date and time of the notification in ISO8601 format.
|
||||
# e.g. 2014-01-01T01:00Z
|
||||
#
|
||||
# @argument account_notification[icon] [Optional, "warning"|"information"|"question"|"error"|"calendar"]
|
||||
# The icon to display with the notification.
|
||||
# Note: Defaults to warning.
|
||||
#
|
||||
# @argument account_notification_roles[] [Optional, String]
|
||||
# The role(s) to send global notification to. Note: ommitting this field will send to everyone
|
||||
# Example:
|
||||
# account_notification_roles: ["StudentEnrollment", "TeacherEnrollment"]
|
||||
#
|
||||
# @example_request
|
||||
# curl -X POST -H 'Authorization: Bearer <token>' \
|
||||
# https://<canvas>/api/v1/accounts/2/account_notifications \
|
||||
# -d 'account_notification[subject]=New notification' \
|
||||
# -d 'account_notification[start_at]=2014-01-01T00:00:00Z' \
|
||||
# -d 'account_notification[end_at]=2014-02-01T00:00:00Z' \
|
||||
# -d 'account_notification[message]=This is a global notification'
|
||||
#
|
||||
# @example_response
|
||||
# {
|
||||
# "subject": "New notification",
|
||||
# "start_at": "2014-01-01T00:00:00Z",
|
||||
# "end_at": "2014-02-01T00:00:00Z",
|
||||
# "message": "This is a global notification"
|
||||
# }
|
||||
def create
|
||||
@notification = AccountNotification.new(params[:account_notification])
|
||||
@notification.account = @account
|
||||
|
@ -13,17 +124,21 @@ class AccountNotificationsController < ApplicationController
|
|||
end
|
||||
respond_to do |format|
|
||||
if @notification.save
|
||||
flash[:notice] = t(:announcement_created_notice, "Announcement successfully created")
|
||||
format.html { redirect_to account_settings_path(@account, :anchor => 'tab-announcements') }
|
||||
format.json { render :json => @notification }
|
||||
if api_request?
|
||||
format.json { render :json => account_notifications_json(@notification, @current_user, session) }
|
||||
else
|
||||
flash[:notice] = t(:announcement_created_notice, "Announcement successfully created")
|
||||
format.html { redirect_to account_settings_path(@account, :anchor => 'tab-announcements') }
|
||||
format.json { render :json => @notification }
|
||||
end
|
||||
else
|
||||
flash[:error] = t(:announcement_creation_failed_notice, "Announcement creation failed")
|
||||
format.html { redirect_to account_settings_path(@account, :anchor => 'tab-announcements') }
|
||||
format.html { redirect_to account_settings_path(@account, :anchor => 'tab-announcements') } unless api_request?
|
||||
format.json { render :json => @notification.errors, :status => :bad_request }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def destroy
|
||||
@notification = @account.announcements.find(params[:id])
|
||||
@notification.destroy
|
||||
|
@ -33,7 +148,7 @@ class AccountNotificationsController < ApplicationController
|
|||
format.json { render :json => @notification }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
def require_account_admin
|
||||
get_context
|
||||
|
@ -44,4 +159,5 @@ class AccountNotificationsController < ApplicationController
|
|||
end
|
||||
return false unless authorized_action(@account, @current_user, :manage_alerts)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -10,8 +10,8 @@ class AccountNotification < ActiveRecord::Base
|
|||
|
||||
EXPORTABLE_ASSOCIATIONS = [:account, :user, :account_notification_roles]
|
||||
|
||||
validates_presence_of :start_at, :end_at, :account_id
|
||||
before_validation :infer_defaults
|
||||
validates_presence_of :start_at, :end_at, :subject, :message, :account_id
|
||||
validate :validate_dates
|
||||
belongs_to :account, :touch => true
|
||||
belongs_to :user
|
||||
has_many :account_notification_roles, dependent: :destroy
|
||||
|
@ -23,10 +23,10 @@ class AccountNotification < ActiveRecord::Base
|
|||
|
||||
validates_inclusion_of :months_in_display_cycle, in: 1..48, allow_nil: true
|
||||
|
||||
def infer_defaults
|
||||
self.start_at ||= Time.now.utc
|
||||
self.end_at ||= self.start_at + 2.weeks
|
||||
self.end_at = [self.end_at, self.start_at].max
|
||||
def validate_dates
|
||||
if self.start_at && self.end_at
|
||||
errors.add(:end_at, t('errors.invalid_account_notification_end_at', "Account notification end time precedes start time")) if self.end_at < self.start_at
|
||||
end
|
||||
end
|
||||
|
||||
def self.for_user_and_account(user, account)
|
||||
|
|
|
@ -846,6 +846,10 @@ routes.draw do
|
|||
get "courses/:course_id/content_list", :controller => :content_exports_api, :action => :content_list, :path_name => "course_content_list"
|
||||
end
|
||||
|
||||
scope(:controller => :account_notifications) do
|
||||
post 'accounts/:account_id/account_notifications', :action => :create, :path_name => 'account_notification'
|
||||
end
|
||||
|
||||
scope(:controller => :tabs) do
|
||||
get "courses/:course_id/tabs", :action => :index, :path_name => 'course_tabs'
|
||||
get "groups/:group_id/tabs", :action => :index, :path_name => 'group_tabs'
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (C) 2011 - 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module Api::V1::AccountNotifications
|
||||
include Api::V1::Json
|
||||
|
||||
def account_notifications_json(account_notification, user, session)
|
||||
roles = account_notification.account_notification_roles.map { |r| r.role_type }
|
||||
json = api_json(account_notification, user, session, :only => %w(subject start_at end_at icon account_notification_roles message))
|
||||
json['roles'] = roles
|
||||
json
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,133 @@
|
|||
#
|
||||
# Copyright (C) 2012 - 2013 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../api_spec_helper')
|
||||
|
||||
describe 'Account Notification API', type: :request do
|
||||
include Api
|
||||
include Api::V1::AccountNotifications
|
||||
|
||||
before do
|
||||
@admin = account_admin_user
|
||||
user_with_pseudonym(:user => @admin)
|
||||
end
|
||||
|
||||
describe 'create' do
|
||||
before :each do
|
||||
@path = "/api/v1/accounts/#{@admin.account.id}/account_notifications"
|
||||
@api_params = { :controller => 'account_notifications',
|
||||
:action => 'create',
|
||||
:format => 'json',
|
||||
:account_id => @admin.account.id.to_s }
|
||||
@start_at = DateTime.now.utc
|
||||
@end_at = (DateTime.now + 1.day).utc
|
||||
end
|
||||
|
||||
it 'should create an account notification' do
|
||||
json = api_call(:post, @path, @api_params,
|
||||
{ :account_notification => {
|
||||
:subject => 'New global notification',
|
||||
:start_at => @start_at.iso8601,
|
||||
:end_at => @end_at.iso8601,
|
||||
:message => 'This is a notification',
|
||||
:icon => 'information'}})
|
||||
json.keys.should include 'start_at'
|
||||
json.keys.should include 'end_at'
|
||||
json['subject'].should == 'New global notification'
|
||||
json['message'].should == 'This is a notification'
|
||||
json['icon'].should == 'information'
|
||||
json['roles'].should == []
|
||||
end
|
||||
|
||||
it 'should default icon to warning' do
|
||||
json = api_call(:post, @path, @api_params,
|
||||
{ :account_notification => {
|
||||
:subject => 'New global notification',
|
||||
:start_at => @start_at.iso8601,
|
||||
:end_at => @end_at.iso8601,
|
||||
:message => 'This is a notification'}})
|
||||
|
||||
json['icon'].should == 'warning'
|
||||
end
|
||||
|
||||
it 'should create an account notification for specific roles' do
|
||||
json = api_call(:post, @path, @api_params,
|
||||
{ :account_notification_roles => ['StudentEnrollment'],
|
||||
:account_notification => {
|
||||
:subject => 'New global notification',
|
||||
:start_at => @start_at.iso8601,
|
||||
:end_at => @end_at.iso8601,
|
||||
:message => 'This is a notification'}})
|
||||
|
||||
notification = AccountNotification.last
|
||||
roles = notification.account_notification_roles
|
||||
roles.count.should == 1
|
||||
roles.first.role_type.should == 'StudentEnrollment'
|
||||
json['roles'].should == ["StudentEnrollment"]
|
||||
end
|
||||
|
||||
it 'should return not authorized for non admin user' do
|
||||
user = user_with_managed_pseudonym
|
||||
begin
|
||||
json = api_call_as_user(user, :post, @path, @api_params,
|
||||
{ :account_notification_roles => ['StudentEnrollment'],
|
||||
:account_notification => {
|
||||
:subject => 'New global notification',
|
||||
:start_at => @start_at.iso8601,
|
||||
:end_at => @end_at.iso8601,
|
||||
:message => 'This is a notification'}})
|
||||
rescue => e
|
||||
e.message.should include 'unauthorized'
|
||||
end
|
||||
end
|
||||
|
||||
it 'should return an error for missing required params' do
|
||||
missing = ['subject', 'message', 'start_at', 'end_at']
|
||||
raw_api_call(:post, @path, @api_params, { :account_notification => { :icon => 'warning'} })
|
||||
response.code.should eql '400'
|
||||
json = JSON.parse(response.body)
|
||||
errors = json['errors'].keys
|
||||
(missing - errors).should be_blank
|
||||
end
|
||||
|
||||
it 'should return an error for malformed dates' do
|
||||
raw_api_call(:post, @path, @api_params,
|
||||
{ :account_notification => {
|
||||
:subject => 'New global notification',
|
||||
:start_at => 'asdrsldkfj',
|
||||
:end_at => 'invalid_date',
|
||||
:message => 'This is a notification',
|
||||
:icon => 'information'}})
|
||||
response.code.should eql '400'
|
||||
end
|
||||
|
||||
it 'should not allow an end date to be before a start date' do
|
||||
raw_api_call(:post, @path, @api_params,
|
||||
{ :account_notification => {
|
||||
:subject => 'New global notification',
|
||||
:start_at => @end_at.iso8601,
|
||||
:end_at => @start_at.iso8601,
|
||||
:message => 'This is a notification',
|
||||
:icon => 'information'}})
|
||||
response.code.should eql '400'
|
||||
errors = JSON.parse(response.body)
|
||||
errors['errors'].keys.should include 'end_at'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -108,25 +108,37 @@ describe "dashboard" do
|
|||
end
|
||||
|
||||
it "should show account notifications on the dashboard" do
|
||||
a1 = @course.account.announcements.create!(:message => "hey there")
|
||||
a2 = @course.account.announcements.create!(:message => "another announcement")
|
||||
a1 = @course.account.announcements.create!(:subject => 'test',
|
||||
:message => "hey there",
|
||||
:start_at => Date.today - 1.day,
|
||||
:end_at => Date.today + 1.day)
|
||||
a2 = @course.account.announcements.create!(:subject => 'test 2',
|
||||
:message => "another annoucement",
|
||||
:start_at => Date.today - 1.day,
|
||||
:end_at => Date.today + 1.day)
|
||||
|
||||
get "/"
|
||||
messages = ffj("#dashboard .global-message .message.user_content")
|
||||
messages.size.should == 2
|
||||
messages[0].text.should == a2.message
|
||||
messages[1].text.should == a1.message
|
||||
messages[0].text.should == a1.message
|
||||
messages[1].text.should == a2.message
|
||||
end
|
||||
|
||||
it "should interpolate the user's domain in global notifications" do
|
||||
announcement = @course.account.announcements.create!(:message => "blah blah http://random-survey-startup.ly/?some_GET_parameter_by_which_to_differentiate_results={{ACCOUNT_DOMAIN}}")
|
||||
announcement = @course.account.announcements.create!(:message => "blah blah http://random-survey-startup.ly/?some_GET_parameter_by_which_to_differentiate_results={{ACCOUNT_DOMAIN}}",
|
||||
:subject => 'test',
|
||||
:start_at => Date.today,
|
||||
:end_at => Date.today + 1.day)
|
||||
|
||||
get "/"
|
||||
fj("#dashboard .global-message .message.user_content").text.should == announcement.message.gsub("{{ACCOUNT_DOMAIN}}", @course.account.domain)
|
||||
end
|
||||
|
||||
it "should interpolate the user's id in global notifications" do
|
||||
announcement = @course.account.announcements.create!(:message => "blah blah http://random-survey-startup.ly/?surveys_are_not_really_anonymous={{CANVAS_USER_ID}}")
|
||||
announcement = @course.account.announcements.create!(:message => "blah blah http://random-survey-startup.ly/?surveys_are_not_really_anonymous={{CANVAS_USER_ID}}",
|
||||
:subject => 'test',
|
||||
:start_at => Date.today,
|
||||
:end_at => Date.today + 1.day)
|
||||
get "/"
|
||||
fj("#dashboard .global-message .message.user_content").text.should == announcement.message.gsub("{{CANVAS_USER_ID}}", @user.global_id.to_s)
|
||||
end
|
||||
|
|
|
@ -762,9 +762,11 @@ end
|
|||
req_service = opts[:required_account_service] || nil
|
||||
roles = opts[:roles] || []
|
||||
message = opts[:message] || "hi there"
|
||||
subj = opts[:subject] || "this is a subject"
|
||||
@account = opts[:account] || Account.default
|
||||
@announcement = @account.announcements.build(message: message, required_account_service: req_service)
|
||||
@announcement = @account.announcements.build(subject: subj, message: message, required_account_service: req_service)
|
||||
@announcement.start_at = opts[:start_at] || 5.minutes.ago.utc
|
||||
@announcement.end_at = opts[:end_at] || 1.day.from_now.utc
|
||||
@announcement.account_notification_roles.build(roles.map { |r| {account_notification_id: @announcement.id, role_type: r} }) unless roles.empty?
|
||||
@announcement.save!
|
||||
end
|
||||
|
|
|
@ -43,7 +43,9 @@ describe "/users/user_dashboard" do
|
|||
assigns[:topics] = []
|
||||
assigns[:upcoming_events] = []
|
||||
assigns[:stream_items] = []
|
||||
assigns[:announcements] = [AccountNotification.create(:subject => "My Global Announcement", :account => Account.default)]
|
||||
assigns[:announcements] = [AccountNotification.create(:message => 'hi', :start_at => Date.today - 1.day,
|
||||
:end_at => Date.today + 2.days,
|
||||
:subject => "My Global Announcement", :account => Account.default)]
|
||||
render "users/user_dashboard"
|
||||
response.body.should match /My Global Announcement/
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue