canvas-lms/lib/api.rb

152 lines
5.3 KiB
Ruby

#
# Copyright (C) 2011 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
# find id in collection, by either id or sis_*_id
# if the collection is over the users table, `self` is replaced by @current_user.id
def api_find(collection, id)
if collection.table_name == User.table_name && id == 'self' && @current_user
id = @current_user.id
end
sis_column, sis_id, sis_find_params = Api.sis_find_params_for_collection(collection, id)
if sis_find_params
collection.first(sis_find_params) || raise(ActiveRecord::RecordNotFound, "Couldn't find #{collection.name} with #{sis_column}=#{sis_id}")
else
collection.find(id)
end
end
# map a list of ids and/or sis ids to plain ids.
def self.map_ids(ids, collection)
ids.map do |id|
sis_column, sis_id, sis_find_params = Api.sis_find_params_for_collection(collection, id)
if sis_find_params
collection.first(sis_find_params.merge(:select => :id)).try(:id)
else
collection.find_by_id(id)
end
end
end
VALID_SIS_COLUMNS = {
Course.table_name =>
{ 'sis_course_id' => 'sis_source_id' },
EnrollmentTerm.table_name =>
{ 'sis_term_id' => 'sis_source_id' },
User.table_name =>
{ 'sis_user_id' => 'pseudonyms.sis_user_id', 'sis_login_id' => 'pseudonyms.sis_source_id' },
Account.table_name =>
{ 'sis_account_id' => 'sis_source_id' },
}
def self.sis_find_params_for_collection(collection, id)
case id
when Numeric
return
else
id = id.to_s
if id =~ %r{^hex:(sis_[\w_]+):(.+)$}
sis_column = $1
sis_id = [$2].pack('H*')
elsif id =~ %r{^(sis_[\w_]+):(.+)$}
sis_column = $1
sis_id = $2
else
return
end
valid_sis_columns = VALID_SIS_COLUMNS[collection.table_name] or
raise(ArgumentError, "need to add support for table name: #{collection.table_name}")
if column = valid_sis_columns[sis_column]
sis_find_params = { :conditions => { column => sis_id } }
# the "user" sis columns are actually on the pseudonym
if collection.table_name == User.table_name
sis_find_params[:include] = :pseudonym
end
return sis_column, sis_id, sis_find_params
else
return
end
end
end
# Add [link HTTP Headers](http://www.w3.org/Protocols/9707-link-header.html) for pagination
# The collection needs to be a will_paginate collection (or act like one)
# a new, paginated collection will be returned
def self.paginate(collection, controller, base_url, pagination_args = {})
per_page = [(controller.params[:per_page] || 10).to_i, Setting.get_cached('api_max_per_page', '50').to_i].min
collection = collection.paginate({ :page => controller.params[:page], :per_page => per_page }.merge(pagination_args))
return unless collection.respond_to?(:next_page)
links = []
template = "<#{base_url}#{base_url =~ /\?/ ? '&': '?'}page=%s&per_page=#{collection.per_page}>; rel=\"%s\""
if collection.next_page
links << template % [collection.next_page, "next"]
end
if collection.previous_page
links << template % [collection.previous_page, "prev"]
end
links << template % [1, "first"]
if collection.total_pages && collection.total_pages > 1
links << template % [collection.total_pages, "last"]
end
controller.response.headers["Link"] = links.join(',') if links.length > 0
collection
end
def attachment_json(attachment, opts={})
url_params = opts[:url_params] || {}
url = case attachment.context_type
when "Course"
course_file_download_url(url_params.merge(:file_id => attachment.id, :id => nil))
when "Group"
group_file_download_url(url_params.merge(:file_id => attachment.id, :id => nil))
when /Submission|User|Assignment/
return nil unless opts[:assignment]
course_assignment_submission_url(@context, opts[:assignment], url_params.merge(:download => attachment.id))
else
return nil
end
{
'content-type' => attachment.content_type,
'display_name' => attachment.display_name,
'filename' => attachment.filename,
'url' => url,
}
end
# stream an array of objects as a json response, without building a string of
# the whole response in memory.
def stream_json_array(array, json_opts)
response.content_type ||= Mime::JSON
render :text => proc { |r, o|
o.write('[')
array.each_with_index { |v,i|
o.write(v.to_json(json_opts));
o.write(',') unless i == array.length - 1
}
o.write(']')
}
end
# See User.submissions_for_given_assignments and SubmissionsApiController#for_students
mattr_accessor :assignment_ids_for_students_api
end