api: allow admins to create/edit/delete enrollment terms

closes CNVS-17700

test plan:
- as an account admin, try creating, editing, and deleting enrollment
  terms
- it should all work
- try it on a sub-account, it should fail
- as someone who doesn't have permission to manage the root account, try
  it on the root account
- it should fail
- do a quick regression test of the terms UI interface

Change-Id: Ibfa45c08ac661e7d905311bc2d66b335be33a5e8
Reviewed-on: https://gerrit.instructure.com/46407
Reviewed-by: Jeremy Stanley <jeremy@instructure.com>
Product-Review: Jeremy Stanley <jeremy@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
QA-Review: Caleb Guanzon <cguanzon@instructure.com>
This commit is contained in:
Simon Williams 2014-12-30 14:01:26 -07:00
parent 03cc4acfeb
commit d425f77307
4 changed files with 234 additions and 37 deletions

View File

@ -18,7 +18,10 @@
# @API Enrollment Terms
#
# API for viewing enrollment terms.
# API for viewing enrollment terms. For all actions, the specified account
# must be a root account and the caller must have permission to manage the
# account (when called on non-root accounts, the errorwill be indicate the
# appropriate root account).
#
# @model EnrollmentTerm
# {
@ -65,9 +68,7 @@ class TermsApiController < ApplicationController
# @API List enrollment terms
#
# Return all of the terms in the account. Account must be a root account and
# requires permission to manage the account (when called on non-root
# accounts, will be directed to the appropriate root account).
# Return all of the terms in the account.
#
# @argument workflow_state[] [String, 'active'| 'deleted'| 'all']
# If set, only returns terms that are in the given state.
@ -83,6 +84,6 @@ class TermsApiController < ApplicationController
terms = terms.where(workflow_state: state) if state.present?
terms = Api.paginate(terms, self, api_v1_enrollment_terms_url)
render json: {enrollment_terms: enrollment_terms_json(terms, @current_user, session)}
render json: { enrollment_terms: enrollment_terms_json(terms, @current_user, session) }
end
end

View File

@ -16,28 +16,69 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# @API Enrollment Terms
class TermsController < ApplicationController
before_filter :require_context, :require_root_account_management
include Api::V1::EnrollmentTerm
def index
@root_account = @context.root_account
@context.default_enrollment_term
@terms = @context.enrollment_terms.active.includes(:enrollment_dates_overrides).order("COALESCE(start_at, created_at) DESC").to_a
end
# @API Create enrollment term
#
# Create a new enrollment term for the specified account.
#
# @argument enrollment_term[name] [String]
# The name of the term.
#
# @argument enrollment_term[start_at] [Timestamp]
# The day/time the term starts.
# Accepts times in ISO 8601 format, e.g. 2015-01-10T18:48:00Z.
#
# @argument enrollment_term[end_at] [Timestamp]
# The day/time the term ends.
# Accepts times in ISO 8601 format, e.g. 2015-01-10T18:48:00Z.
#
# @returns EnrollmentTerm
#
def create
overrides = params[:enrollment_term].delete(:overrides) rescue nil
@term = @context.enrollment_terms.active.build(params[:enrollment_term])
if @term.save
@term.set_overrides(@context, overrides)
render :json => @term.as_json(:include => :enrollment_dates_overrides)
if api_request?
render :json => enrollment_term_json(@term, @current_user, session)
else
render :json => @term.as_json(:include => :enrollment_dates_overrides)
end
else
render :json => @term.errors, :status => :bad_request
end
end
# @API Update enrollment term
#
# Update an existing enrollment term for the specified account.
#
# @argument enrollment_term[name] [String]
# The name of the term.
#
# @argument enrollment_term[start_at] [Timestamp]
# The day/time the term starts.
# Accepts times in ISO 8601 format, e.g. 2015-01-10T18:48:00Z.
#
# @argument enrollment_term[end_at] [Timestamp]
# The day/time the term ends.
# Accepts times in ISO 8601 format, e.g. 2015-01-10T18:48:00Z.
#
# @returns EnrollmentTerm
#
def update
overrides = params[:enrollment_term].delete(:overrides) rescue nil
@term = @context.enrollment_terms.active.find(params[:id])
@term = api_find(@context.enrollment_terms.active, params[:id])
root_account = @context.root_account
if sis_id = params[:enrollment_term].delete(:sis_source_id)
if sis_id != @account.sis_source_id && root_account.grants_right?(@current_user, session, :manage_sis)
@ -50,15 +91,29 @@ class TermsController < ApplicationController
end
if @term.update_attributes(params[:enrollment_term])
@term.set_overrides(@context, overrides)
render :json => @term.as_json(:include => :enrollment_dates_overrides)
if api_request?
render :json => enrollment_term_json(@term, @current_user, session)
else
render :json => @term.as_json(:include => :enrollment_dates_overrides)
end
else
render :json => @term.errors, :status => :bad_request
end
end
# @API Delete enrollment term
#
# Delete the specified enrollment term.
#
# @returns EnrollmentTerm
#
def destroy
@term = @context.enrollment_terms.find(params[:id])
@term = api_find(@context.enrollment_terms, params[:id])
@term.destroy
render :json => @term
if api_request?
render :json => enrollment_term_json(@term, @current_user, session)
else
render :json => @term
end
end
end

View File

@ -485,7 +485,7 @@ CanvasRails::Application.routes.draw do
end
end
resources :terms
resources :terms, except: [:show, :new, :edit]
resources :sub_accounts
get :avatars
@ -835,6 +835,12 @@ CanvasRails::Application.routes.draw do
get 'accounts/:account_id/terms', action: :index, as: 'enrollment_terms'
end
scope(controller: :terms) do
post 'accounts/:account_id/terms', action: :create
put 'accounts/:account_id/terms/:id', action: :update
delete 'accounts/:account_id/terms/:id', action: :destroy
end
scope(controller: :authentication_audit_api) do
get 'audit/authentication/logins/:login_id', action: :for_login, as: 'audit_authentication_login'
get 'audit/authentication/accounts/:account_id', action: :for_account, as: 'audit_authentication_account'

View File

@ -28,17 +28,19 @@ describe TermsApiController, type: :request do
@term2 = @account.enrollment_terms.create(name: "Term 2")
end
def get_terms(options={})
def get_terms(body_params={})
json = api_call(:get, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms_api', action: 'index', format: 'json', account_id: @account.to_param },
options)
body_params)
json['enrollment_terms']
end
describe "filtering by state" do
it "should list all active terms by default" do
before :once do
@term2.destroy
end
it "should list all active terms by default" do
json = get_terms
names = json.map{ |t| t['name'] }
expect(names).to include(@term1.name)
@ -46,8 +48,6 @@ describe TermsApiController, type: :request do
end
it "should list active terms with state=active" do
@term2.destroy
json = get_terms(workflow_state: 'active')
names = json.map{ |t| t['name'] }
expect(names).to include(@term1.name)
@ -55,8 +55,6 @@ describe TermsApiController, type: :request do
end
it "should list deleted terms with state=deleted" do
@term2.destroy
json = get_terms(workflow_state: 'deleted')
names = json.map{ |t| t['name'] }
expect(names).not_to include(@term1.name)
@ -64,8 +62,6 @@ describe TermsApiController, type: :request do
end
it "should list all terms, active and deleted, with state=all" do
@term2.destroy
json = get_terms(workflow_state: 'all')
names = json.map{ |t| t['name'] }
expect(names).to include(@term1.name)
@ -73,8 +69,6 @@ describe TermsApiController, type: :request do
end
it "should list all terms, active and deleted, with state=[all]" do
@term2.destroy
json = get_terms(workflow_state: ['all'])
names = json.map{ |t| t['name'] }
expect(names).to include(@term1.name)
@ -87,11 +81,9 @@ describe TermsApiController, type: :request do
@term1.update_attributes(start_at: 1.day.ago, end_at: 5.days.from_now)
@term2.update_attributes(start_at: 2.days.ago, end_at: 6.days.from_now)
json = api_call(:get, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms_api', action: 'index', format: 'json', account_id: @account.to_param })
expect(json['enrollment_terms'].first['name']).to eq @term2.name
expect(json['enrollment_terms'].last['name']).to eq @term1.name
json = get_terms
expect(json.first['name']).to eq @term2.name
expect(json.last['name']).to eq @term1.name
end
it "should order by end_at second" do
@ -99,11 +91,9 @@ describe TermsApiController, type: :request do
@term1.update_attributes(start_at: start_at, end_at: 6.days.from_now)
@term2.update_attributes(start_at: start_at, end_at: 5.days.from_now)
json = api_call(:get, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms_api', action: 'index', format: 'json', account_id: @account.to_param })
expect(json['enrollment_terms'].first['name']).to eq @term2.name
expect(json['enrollment_terms'].last['name']).to eq @term1.name
json = get_terms
expect(json.first['name']).to eq @term2.name
expect(json.last['name']).to eq @term1.name
end
it "should order by id last" do
@ -112,11 +102,9 @@ describe TermsApiController, type: :request do
@term1.update_attributes(start_at: start_at, end_at: end_at)
@term2.update_attributes(start_at: start_at, end_at: end_at)
json = api_call(:get, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms_api', action: 'index', format: 'json', account_id: @account.to_param })
expect(json['enrollment_terms'].first['name']).to eq @term1.name
expect(json['enrollment_terms'].last['name']).to eq @term2.name
json = get_terms
expect(json.first['name']).to eq @term1.name
expect(json.last['name']).to eq @term2.name
end
end
@ -126,5 +114,152 @@ describe TermsApiController, type: :request do
expect(response.headers).to include('Link')
expect(response.headers['Link']).to match(/rel="next"/)
end
describe "authorization" do
def expect_terms_index_401
api_call(:get, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms_api', action: 'index', format: 'json', account_id: @account.to_param },
{},
{},
{ expected_status: 401 })
end
it "should require auth for the right account" do
other_account = Account.create(name: 'other')
account_admin_user(account: other_account)
expect_terms_index_401
end
it "should require root domain auth" do
subaccount = @account.sub_accounts.create!(name: 'subaccount')
account_admin_user(account: subaccount)
expect_terms_index_401
end
end
end
end
describe TermsController, type: :request do
before :once do
@account = Account.create(name: 'new')
account_admin_user(account: @account)
@account.enrollment_terms.scoped.delete_all
@term1 = @account.enrollment_terms.create(name: "Term 1")
end
describe "create" do
it "should allow creating a term" do
start_at = 3.days.ago
end_at = 3.days.from_now
json = api_call(:post, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms', action: 'create', format: 'json', account_id: @account.to_param },
{ enrollment_term: { name: 'Term 2', start_at: start_at.iso8601, end_at: end_at.iso8601 } })
expect(json['id']).to be_present
expect(json['name']).to eq 'Term 2'
expect(json['start_at']).to eq start_at.iso8601
expect(json['end_at']).to eq end_at.iso8601
new_term = @account.reload.enrollment_terms.find(json['id'])
expect(new_term.name).to eq 'Term 2'
expect(new_term.start_at.to_i).to eq start_at.to_i
expect(new_term.end_at.to_i).to eq end_at.to_i
end
describe "authorization" do
def expect_terms_create_401
api_call(:post, "/api/v1/accounts/#{@account.id}/terms",
{ controller: 'terms', action: 'create', format: 'json', account_id: @account.to_param },
{ enrollment_term: { name: 'Term 2' } },
{},
{ expected_status: 401 })
end
it "should require auth for the right account" do
other_account = Account.create(name: 'other')
account_admin_user(account: other_account)
expect_terms_create_401
end
it "should require root domain auth" do
subaccount = @account.sub_accounts.create!(name: 'subaccount')
account_admin_user(account: subaccount)
expect_terms_create_401
end
end
end
describe "update" do
it "should allow updating a term" do
start_at = 3.days.ago
end_at = 3.days.from_now
json = api_call(:put, "/api/v1/accounts/#{@account.id}/terms/#{@term1.id}",
{ controller: 'terms', action: 'update', format: 'json', account_id: @account.to_param, id: @term1.to_param },
{ enrollment_term: { name: 'Term 2', start_at: start_at.iso8601, end_at: end_at.iso8601 } })
expect(json['id']).to eq @term1.id
expect(json['name']).to eq 'Term 2'
expect(json['start_at']).to eq start_at.iso8601
expect(json['end_at']).to eq end_at.iso8601
@term1.reload
expect(@term1.name).to eq 'Term 2'
expect(@term1.start_at.to_i).to eq start_at.to_i
expect(@term1.end_at.to_i).to eq end_at.to_i
end
describe "authorization" do
def expect_terms_update_401
api_call(:put, "/api/v1/accounts/#{@account.id}/terms/#{@term1.id}",
{ controller: 'terms', action: 'update', format: 'json', account_id: @account.to_param, id: @term1.to_param},
{ enrollment_term: { name: 'Term 2' } },
{},
{ :expected_status => 401 })
end
it "should require auth for the right account" do
other_account = Account.create(name: 'other')
account_admin_user(account: other_account)
expect_terms_update_401
end
it "should require root domain auth" do
subaccount = @account.sub_accounts.create!(name: 'subaccount')
account_admin_user(account: subaccount)
expect_terms_update_401
end
end
end
describe "destroy" do
it "should allow deleting a term" do
json = api_call(:delete, "/api/v1/accounts/#{@account.id}/terms/#{@term1.id}",
{ controller: 'terms', action: 'destroy', format: 'json', account_id: @account.to_param, id: @term1.to_param })
expect(json['id']).to eq @term1.id
expect(@term1.reload).to be_deleted
end
describe "authorization" do
def expect_terms_destroy_401
api_call(:delete, "/api/v1/accounts/#{@account.id}/terms/#{@term1.id}",
{ controller: 'terms', action: 'destroy', format: 'json', account_id: @account.to_param, id: @term1.to_param },
{},
{},
{ :expected_status => 401 })
end
it "should require auth for the right account" do
other_account = Account.create(name: 'other')
account_admin_user(account: other_account)
expect_terms_destroy_401
end
it "should require root domain auth" do
subaccount = @account.sub_accounts.create!(name: 'subaccount')
account_admin_user(account: subaccount)
expect_terms_destroy_401
end
end
end
end