Mass Disable Sync to SIS Backend

Refs: SIS-2708

Test plan:
- Activate the api one of the following ways:
-- Install a post_grades lti tool
-- Enable the bulk_sis_grade_export feature and enable the
   New SIS Integrations feature
- PUT /api/sis/courses/:course_id/disable_post_to_sis
-- Assignments for the course will have the `post_to_sis`
   attribute set to false
- Repeat previous step and send in a valid grading period id
-- Assignments for the course inside the particular grading
   will have the `post_to_sis` attribute set to false

Change-Id: I512f99943463a2d15278a1bdb3a60ed0c19034ec
Reviewed-on: https://gerrit.instructure.com/111164
Reviewed-by: Tyler Pickett <tpickett@instructure.com>
Reviewed-by: Brad Humphrey <brad@instructure.com>
Tested-by: Jenkins
QA-Review: Mark McDermott <mmcdermott@instructure.com>
Product-Review: Brad Humphrey <brad@instructure.com>
This commit is contained in:
Nick Houle 2017-05-08 16:07:09 -06:00
parent 1644463879
commit cc0ae2b7a2
3 changed files with 247 additions and 0 deletions

View File

@ -0,0 +1,127 @@
#
# Copyright (C) 2017 - present 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 SIS Integration
#
# Includes helpers for integration with SIS systems.
#
class DisablePostToSisApiController < ApplicationController
before_action :require_authorized_user
before_action :require_post_to_sis
before_action :require_published_course
before_action :require_valid_grading_period
# @API Disable assignments currently enabled for grade export to SIS
# @beta
#
# Disable all assignments flagged as "post_to_sis", with the option of making it
# specific to a grading period, in a course.
#
# @argument course_id [Integer] The ID of the course.
#
# @argument grading_period_id [Integer] The ID of the grading period.
#
# On success, the response will be 204 No Content with an empty body.
#
# On failure, the response will be 400 Bad Request with a body of a specific
# message.
#
# @example_request
#
# curl 'https://<canvas>/api/sis/courses/<course_id>/disable_post_to_sis' \
# -X PUT \
# -H "Authorization: Bearer <token>" \
# -H "Content-Length: 0"
#
# For disabling assignments in a specific grading period
#
# @example_request
#
# curl 'https://<canvas>/api/sis/courses/<course_id>/disable_post_to_sis' \
# -X PUT \
# -H "Authorization: Bearer <token>" \
# -H "Content-Length: 0" \
# -d 'grading_period_id=1'
#
def disable_post_to_sis
assignments = published_assignments.where(post_to_sis: true).limit(1000)
while assignments.update_all(post_to_sis: false) > 0 do end
head :no_content
end
private
def context
@context ||=
if params[:course_id]
api_find(Course, params[:course_id])
else
fail ActiveRecord::RecordNotFound, 'unknown context type'
end
end
def grading_period
@grading_period ||=
GradingPeriod.for(context, inherit: true).find_by(id: params[:grading_period_id])
end
def published_assignments
assignments = Assignment.published.for_course(context)
if grading_period
assignments.where("due_at BETWEEN ? AND ? OR due_at IS NULL",
grading_period.start_date, grading_period.end_date)
else
assignments
end
end
def post_to_sis_enabled?
# We'll need to remove sis_grade_export_enabled?
# check when we deprecate the feature option
Assignment.sis_grade_export_enabled?(context) &&
AssignmentUtil.sis_integration_settings_enabled?(context)
end
def require_authorized_user
head :unauthorized unless context.grants_right?(@current_user, session, :manage_assignments)
end
def require_valid_grading_period
body = {
code: 'not_found',
error: I18n.t('The Grading Period cannot be found')
}
render json: body, status: :bad_request if params[:grading_period_id] && grading_period.blank?
end
def require_post_to_sis
body = {
code: 'not_enabled',
error: I18n.t('A SIS integration is not configured and the Enable new SIS integration settings feature is not enabled')
}
render json: body, status: :bad_request unless post_to_sis_enabled?
end
def require_published_course
body = {
code: 'unpublished_course',
error: I18n.t('Disabling Post to SIS is not available for non-published courses')
}
render json: body, status: :bad_request if context.is_a?(Course) && !context.published?
end
end

View File

@ -2057,5 +2057,8 @@ CanvasRails::Application.routes.draw do
get 'accounts/:account_id/assignments', action: 'sis_assignments', as: :sis_account_assignments
get 'courses/:course_id/assignments', action: 'sis_assignments', as: :sis_course_assignments
end
scope(controller: :disable_post_to_sis_api) do
put 'courses/:course_id/disable_post_to_sis', action: 'disable_post_to_sis', as: :disable_post_to_sis_course_assignments
end
end
end

View File

@ -0,0 +1,117 @@
#
# Copyright (C) 2017 - present 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__) + '/../spec_helper')
describe DisablePostToSisApiController do
describe "PUT disable_post_to_sis" do
let(:account) {account_model}
let(:course) {course_model(account: account, workflow_state: 'available')}
let(:admin) {account_admin_user(account: account)}
before do
bypass_rescue
user_session(admin)
end
it 'responds with 400 when post_to_sis is disabled' do
put 'disable_post_to_sis', course_id: course.id
parsed_json = json_parse(response.body)
expect(response.code).to eq "400"
expect(parsed_json['code']).to eq 'not_enabled'
end
context 'with bulk_sis_grade_export and new_sis_integrations enabled' do
before do
account.enable_feature!(:bulk_sis_grade_export)
account.enable_feature!(:new_sis_integrations)
end
it 'responds with 400 when course is unpublished' do
course.workflow_state = 'unpublished'
course.save!
put 'disable_post_to_sis', course_id: course.id
parsed_json = json_parse(response.body)
expect(response.code).to eq "400"
expect(parsed_json['code']).to eq 'unpublished_course'
end
it 'responds with 200' do
put 'disable_post_to_sis', course_id: course.id
expect(response.code).to eq "204"
expect(response.success?).to be_truthy
end
it 'disables assignments with post_to_sis enabled' do
assignment = assignment_model(course: course,
post_to_sis: true,
workflow_state: 'published')
put 'disable_post_to_sis', course_id: course.id
assignment = Assignment.find(assignment.id)
expect(response.code).to eq "204"
expect(response.success?).to be_truthy
expect(assignment.post_to_sis).to be_falsey
end
context 'with assignments in a grading_period' do
let(:grading_period_group) do
group = account.grading_period_groups.create!(title: "A Group")
term = course.enrollment_term
group.enrollment_terms << term
group
end
let(:grading_period) do
grading_period_group.grading_periods.create!(
title: 'Too Much Tuna',
start_date: 2.months.from_now(Time.zone.now),
end_date: 3.months.from_now(Time.zone.now)
)
end
it 'responds with 400 when grading period does not exist' do
put 'disable_post_to_sis', course_id: course.id,
grading_period_id: 789465789
parsed_json = json_parse(response.body)
expect(response.code).to eq "400"
expect(parsed_json['code']).to eq 'not_found'
end
it 'disables assignments with post_to_sis enabled based on grading period' do
assignment = assignment_model(course: course,
post_to_sis: true,
workflow_state: 'published',
due_at: grading_period.start_date + 1.minute)
put 'disable_post_to_sis', course_id: course.id,
grading_period_id: grading_period.id
assignment = Assignment.find(assignment.id)
expect(response.code).to eq "204"
expect(response.success?).to be_truthy
expect(assignment.post_to_sis).to be_falsey
end
end
end
end
end