Add submisson, attachment, and report id variable expansions
Closes PLAT-2688 Test Plan: - Install an plagiarism detection tool that uses LTI launches for displaying originality reports. The message handler for these launches should have com.instructure.OriginalityReport.id, com.instructure.Submission.id, and com.instructure.File.id in it's enabled capability array. - Create an originality report with the tool and launch the originality report. - Verify that parameters for each of these three capabilities are send and set correctly. Change-Id: I2cb246e3a48f5e63a60ff6a0d90a003aaf9c8d62 Reviewed-on: https://gerrit.instructure.com/116377 Reviewed-by: Andrew Butterfield <abutterfield@instructure.com> Reviewed-by: Nathan Mills <nathanm@instructure.com> Tested-by: Jenkins QA-Review: August Thornton <august@instructure.com> Product-Review: Weston Dransfield <wdransfield@instructure.com>
This commit is contained in:
parent
bbe4a41738
commit
55fce28b93
|
@ -151,8 +151,9 @@ module Lti
|
|||
tag = find_tag
|
||||
custom_param_opts = prep_tool_settings(message_handler.parameters, tool_proxy, launch_params[:resource_link_id])
|
||||
custom_param_opts[:content_tag] = tag if tag
|
||||
|
||||
variable_expander = create_variable_expander(custom_param_opts.merge(tool: tool_proxy))
|
||||
tool_setting = ToolSetting.find_by(resource_link_id: resource_link_id)
|
||||
variable_expander = create_variable_expander(custom_param_opts.merge(tool: tool_proxy,
|
||||
tool_setting: tool_setting))
|
||||
launch_params.merge! enabled_parameters(tool_proxy, message_handler, variable_expander)
|
||||
|
||||
message = IMS::LTI::Models::Messages::BasicLTILaunchRequest.new(launch_params)
|
||||
|
|
|
@ -152,22 +152,13 @@ module Lti
|
|||
# @returns OriginalityReport
|
||||
def create
|
||||
render_unauthorized_action and return unless tool_proxy_associated?
|
||||
report_attributes = params.require(:originality_report).permit(create_attributes).to_hash.merge(
|
||||
{submission_id: params.require(:submission_id)}
|
||||
)
|
||||
|
||||
@report = OriginalityReport.new(build_link_id!(report_attributes))
|
||||
begin
|
||||
successful_save = @report.save
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
@report.errors.add(:base, I18n.t('the specified file with file_id already has an originality report'))
|
||||
end
|
||||
|
||||
if successful_save
|
||||
render json: api_json(@report, @current_user, session), status: :created
|
||||
else
|
||||
render json: @report.errors, status: :bad_request
|
||||
end
|
||||
@report = OriginalityReport.create!(create_report_params)
|
||||
render json: api_json(@report, @current_user, session), status: :created
|
||||
rescue ActiveRecord::RecordInvalid
|
||||
return render json: @report.errors, status: :bad_request
|
||||
rescue ActiveRecord::RecordNotUnique
|
||||
return render json: {error: {message: I18n.t('the specified file with file_id already has an originality report'),
|
||||
type: 'RecordNotUnique'}}, status: :bad_request
|
||||
end
|
||||
|
||||
# @API Edit an Originality Report
|
||||
|
@ -205,7 +196,7 @@ module Lti
|
|||
# @returns OriginalityReport
|
||||
def update
|
||||
render_unauthorized_action and return unless tool_proxy_associated?
|
||||
if @report.update_attributes(build_link_id!(params.require(:originality_report).permit(update_attributes)))
|
||||
if @report.update_attributes(update_report_params)
|
||||
render json: api_json(@report, @current_user, session)
|
||||
else
|
||||
render json: @report.errors, status: :bad_request
|
||||
|
@ -227,20 +218,18 @@ module Lti
|
|||
|
||||
private
|
||||
|
||||
def build_link_id!(report_hash)
|
||||
resource_type_code = report_hash.dig('tool_setting', 'resource_type_code')
|
||||
def link_id(tool_setting_params)
|
||||
resource_type_code = tool_setting_params&.dig('resource_type_code')
|
||||
if resource_type_code.present?
|
||||
rh = tool_proxy.resources.find_by(resource_type_code: resource_type_code)
|
||||
link_id = resource_link_id(rh, report_hash.dig('tool_setting', 'resource_url'))
|
||||
report_hash['link_id'] = link_id
|
||||
resource_link_id(rh, tool_setting_params['resource_url'])
|
||||
end
|
||||
report_hash.delete 'tool_setting'
|
||||
report_hash
|
||||
end
|
||||
|
||||
def resource_link_id(resource_handler, lti_url = nil)
|
||||
tool_setting = resource_handler.find_or_create_tool_setting(resource_url: lti_url,
|
||||
link_fragment: link_fragment)
|
||||
link_fragment: link_fragment,
|
||||
context: attachment_association)
|
||||
tool_setting.resource_link_id
|
||||
end
|
||||
|
||||
|
@ -282,8 +271,36 @@ module Lti
|
|||
@_submission ||= Submission.active.find(params[:submission_id])
|
||||
end
|
||||
|
||||
def attachment
|
||||
@_attachment ||= Attachment.find(params.require(:originality_report)[:file_id])
|
||||
end
|
||||
|
||||
def attachment_association
|
||||
@_attachment_association ||= begin
|
||||
file = @report&.attachment || attachment
|
||||
file.attachment_associations.find { |a| a.context == submission }
|
||||
end
|
||||
end
|
||||
|
||||
def create_report_params
|
||||
@_create_report_params ||= begin
|
||||
report_attributes = params.require(:originality_report).permit(create_attributes).to_hash.merge(
|
||||
{submission_id: params.require(:submission_id)}
|
||||
)
|
||||
report_attributes['link_id'] = link_id(report_attributes.delete('tool_setting'))
|
||||
report_attributes
|
||||
end
|
||||
end
|
||||
|
||||
def update_report_params
|
||||
@_update_report_params ||= begin
|
||||
report_attributes = params.require(:originality_report).permit(update_attributes)
|
||||
report_attributes['link_id'] = link_id(report_attributes.delete('tool_setting'))
|
||||
report_attributes
|
||||
end
|
||||
end
|
||||
|
||||
def attachment_in_context
|
||||
attachment = Attachment.find(params.require(:originality_report)[:file_id])
|
||||
verify_submission_attachment(attachment, submission)
|
||||
end
|
||||
|
||||
|
|
|
@ -54,11 +54,9 @@ module Lti
|
|||
Canvas.placements.postGrades
|
||||
Security.splitSecret
|
||||
Context.sourcedId
|
||||
).concat(
|
||||
CapabilitiesHelper::SUPPORTED_CAPABILITIES
|
||||
).concat(
|
||||
Lti::VariableExpander.expansion_keys
|
||||
).uniq.freeze
|
||||
).freeze
|
||||
|
||||
RESTRICTED_CAPABILITIES = [
|
||||
'Canvas.placements.similarityDetection',
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
module Lti
|
||||
class ToolSetting < ActiveRecord::Base
|
||||
belongs_to :tool_proxy
|
||||
belongs_to :context, polymorphic: [:course, :account]
|
||||
belongs_to :context, polymorphic: [:course, :account, :attachment_association]
|
||||
|
||||
validates_presence_of :context, if: :has_resource_link_id?
|
||||
|
||||
|
|
|
@ -108,6 +108,36 @@ particular placement:
|
|||
```
|
||||
|
||||
# Supported Substitutions
|
||||
## com.instructure.OriginalityReport.id
|
||||
The Canvas id of the Originality Report associated
|
||||
with the launch.
|
||||
|
||||
**Availability**: **
|
||||
**Launch Parameter**: *com_instructure_originality_report_id*
|
||||
|
||||
```
|
||||
23
|
||||
```
|
||||
## com.instructure.Submission.id
|
||||
The Canvas id of the submission associated with the
|
||||
launch.
|
||||
|
||||
**Availability**: **
|
||||
**Launch Parameter**: *com_instructure_submission_id*
|
||||
|
||||
```
|
||||
23
|
||||
```
|
||||
## com.instructure.File.id
|
||||
The Canvas id of the file associated with the submission
|
||||
in the launch.
|
||||
|
||||
**Availability**: **
|
||||
**Launch Parameter**: *com_instructure_file_id*
|
||||
|
||||
```
|
||||
23
|
||||
```
|
||||
## CourseOffering.sourcedId
|
||||
the LIS identifier for the course offering.
|
||||
|
||||
|
|
|
@ -17,27 +17,12 @@
|
|||
|
||||
module Lti
|
||||
class CapabilitiesHelper
|
||||
SUPPORTED_CAPABILITIES = %w(ToolConsumerInstance.guid
|
||||
CourseSection.sourcedId
|
||||
Membership.role
|
||||
Person.email.primary
|
||||
Person.name.given
|
||||
Person.name.family
|
||||
Person.name.full
|
||||
Person.sourcedId
|
||||
User.id
|
||||
User.image
|
||||
Message.documentTarget
|
||||
Message.locale
|
||||
Context.id
|
||||
vnd.Canvas.root_account.uuid).freeze
|
||||
|
||||
def self.supported_capabilities
|
||||
SUPPORTED_CAPABILITIES
|
||||
VariableExpander.default_name_expansions
|
||||
end
|
||||
|
||||
def self.filter_capabilities(enabled_capability)
|
||||
enabled_capability & SUPPORTED_CAPABILITIES
|
||||
enabled_capability & supported_capabilities
|
||||
end
|
||||
|
||||
def self.capability_params_hash(enabled_capability, variable_expander)
|
||||
|
|
|
@ -44,6 +44,10 @@ module Lti
|
|||
self.expansions.keys.map { |c| c.to_s[1..-1] }
|
||||
end
|
||||
|
||||
def self.default_name_expansions
|
||||
self.expansions.values.select { |v| v.default_name.present? }.map(&:name)
|
||||
end
|
||||
|
||||
CONTROLLER_GUARD = -> { !!@controller }
|
||||
COURSE_GUARD = -> { @context.is_a? Course }
|
||||
TERM_START_DATE_GUARD = -> { @context.is_a?(Course) && @context.enrollment_term &&
|
||||
|
@ -61,6 +65,7 @@ module Lti
|
|||
MEDIA_OBJECT_ID_GUARD = -> {@attachment && (@attachment.media_object || @attachment.media_entry_id )}
|
||||
LTI1_GUARD = -> { @tool.is_a?(ContextExternalTool) }
|
||||
MASQUERADING_GUARD = -> { !!@controller && @controller.logged_in_user != @current_user }
|
||||
ATTACHMENT_ASSOCIATION_GUARD = -> { @tool_setting&.context&.is_a?(AttachmentAssociation) }
|
||||
|
||||
def initialize(root_account, context, controller, opts = {})
|
||||
@root_account = root_account
|
||||
|
@ -107,6 +112,46 @@ module Lti
|
|||
end
|
||||
end
|
||||
|
||||
# The Canvas id of the Originality Report associated
|
||||
# with the launch.
|
||||
# @launch_parameter com_instructure_originality_report_id
|
||||
# @example
|
||||
# ```
|
||||
# 23
|
||||
# ```
|
||||
register_expansion 'com.instructure.OriginalityReport.id', [],
|
||||
-> do
|
||||
@tool_setting.context.context.originality_reports.find do |r|
|
||||
r.attachment_id == @tool_setting.context.attachment_id
|
||||
end.id
|
||||
end,
|
||||
ATTACHMENT_ASSOCIATION_GUARD,
|
||||
default_name: 'com_instructure_originality_report_id'
|
||||
|
||||
# The Canvas id of the submission associated with the
|
||||
# launch.
|
||||
# @launch_parameter com_instructure_submission_id
|
||||
# @example
|
||||
# ```
|
||||
# 23
|
||||
# ```
|
||||
register_expansion 'com.instructure.Submission.id', [],
|
||||
-> { @tool_setting.context.context_id },
|
||||
ATTACHMENT_ASSOCIATION_GUARD,
|
||||
default_name: 'com_instructure_submission_id'
|
||||
|
||||
# The Canvas id of the file associated with the submission
|
||||
# in the launch.
|
||||
# @launch_parameter com_instructure_file_id
|
||||
# @example
|
||||
# ```
|
||||
# 23
|
||||
# ```
|
||||
register_expansion 'com.instructure.File.id', [],
|
||||
-> { @tool_setting.context.attachment_id },
|
||||
ATTACHMENT_ASSOCIATION_GUARD,
|
||||
default_name: 'com_instructure_file_id'
|
||||
|
||||
# the LIS identifier for the course offering
|
||||
# @launch_parameter lis_course_offering_sourcedid
|
||||
# @example
|
||||
|
|
|
@ -217,7 +217,6 @@ module Lti
|
|||
report_file.save!
|
||||
|
||||
put @endpoints[:update], {originality_report: {originality_report_file_id: report_file.id}}, request_headers
|
||||
|
||||
expect(response).to be_success
|
||||
expect(OriginalityReport.find(@report.id).originality_report_file_id).to eq report_file.id
|
||||
end
|
||||
|
@ -241,7 +240,6 @@ module Lti
|
|||
}
|
||||
},
|
||||
request_headers
|
||||
|
||||
expect(response).to be_success
|
||||
tool_setting = Lti::ToolSetting.find_by(resource_link_id: OriginalityReport.find(@report.id).link_id)
|
||||
expect(tool_setting.resource_url).to eq "http://www.lti-test.com"
|
||||
|
@ -291,6 +289,26 @@ module Lti
|
|||
expect(response_body['tool_setting']['resource_type_code']).to eq resource_handler.resource_type_code
|
||||
end
|
||||
|
||||
it 'sets the context for the associated tool setting' do
|
||||
score = 0.25
|
||||
put @endpoints[:update],
|
||||
{
|
||||
originality_report: {
|
||||
file_id: @attachment.id,
|
||||
originality_score: score,
|
||||
tool_setting: {
|
||||
resource_type_code: resource_handler.resource_type_code
|
||||
}
|
||||
}
|
||||
},
|
||||
request_headers
|
||||
response_body = JSON.parse(response.body)
|
||||
report = OriginalityReport.find(response_body['id'])
|
||||
tool_setting = Lti::ToolSetting.find_by(resource_link_id: report.link_id)
|
||||
attachment_association = report.attachment.attachment_associations.first
|
||||
expect(tool_setting.context).to eq attachment_association
|
||||
end
|
||||
|
||||
it 'sets the workflow state' do
|
||||
put @endpoints[:update],
|
||||
{
|
||||
|
@ -394,7 +412,7 @@ module Lti
|
|||
|
||||
post @endpoints[:create], {originality_report: {file_id: @attachment.id, originality_score: 0.4}}, request_headers
|
||||
expect(response.status).to eq 400
|
||||
expect(JSON.parse(response.body)['errors'].key?('base')).to be_truthy
|
||||
expect(JSON.parse(response.body)['error']['type']).to eq 'RecordNotUnique'
|
||||
end
|
||||
|
||||
it "requires the plagiarism feature flag" do
|
||||
|
@ -440,6 +458,24 @@ module Lti
|
|||
expect(response_body['tool_setting']['resource_type_code']).to eq resource_handler.resource_type_code
|
||||
end
|
||||
|
||||
it 'sets the context for the associated tool setting' do
|
||||
post @endpoints[:create],
|
||||
{
|
||||
originality_report: {
|
||||
file_id: @attachment.id,
|
||||
tool_setting: {
|
||||
resource_type_code: resource_handler.resource_type_code
|
||||
}
|
||||
}
|
||||
},
|
||||
request_headers
|
||||
response_body = JSON.parse(response.body)
|
||||
report = OriginalityReport.find(response_body['id'])
|
||||
tool_setting = Lti::ToolSetting.find_by(resource_link_id: report.link_id)
|
||||
attachment_association = report.attachment.attachment_associations.first
|
||||
expect(tool_setting.context).to eq attachment_association
|
||||
end
|
||||
|
||||
it 'sets the workflow state' do
|
||||
post @endpoints[:create],
|
||||
{
|
||||
|
|
|
@ -86,7 +86,15 @@ module Lti
|
|||
Message.documentTarget
|
||||
Message.locale
|
||||
Context.id
|
||||
vnd.Canvas.root_account.uuid)
|
||||
CourseOffering.sourcedId
|
||||
com.instructure.File.id
|
||||
com.instructure.OriginalityReport.id
|
||||
com.instructure.Submission.id
|
||||
com.instructure.contextLabel
|
||||
vnd.Canvas.root_account.uuid
|
||||
vnd.Canvas.OriginalityReport.url
|
||||
vnd.Canvas.submission.history.url
|
||||
vnd.Canvas.submission.url)
|
||||
}
|
||||
describe '#supported_capabilities' do
|
||||
it 'returns all supported capabilities asociated with launch params' do
|
||||
|
|
|
@ -68,8 +68,17 @@ module Lti
|
|||
m.stubs(:view_context).returns(view_context_mock)
|
||||
m
|
||||
end
|
||||
|
||||
let(:variable_expander) { VariableExpander.new(root_account, account, controller, current_user: user, tool: tool) }
|
||||
let(:attachment) { attachment_model }
|
||||
let(:submission) { submission_model }
|
||||
let(:resource_link_id) { SecureRandom.uuid }
|
||||
let(:originality_report) do
|
||||
OriginalityReport.create!(attachment: attachment,
|
||||
submission: submission,
|
||||
link_id: resource_link_id)
|
||||
end
|
||||
let(:attachment_association) { AttachmentAssociation.create!(context: submission, attachment: attachment) }
|
||||
let(:tool_setting) { Lti::ToolSetting.create!(context: attachment_association, resource_link_id: resource_link_id) }
|
||||
let(:variable_expander) { VariableExpander.new(root_account, account, controller, current_user: user, tool: tool, tool_setting: tool_setting) }
|
||||
|
||||
it 'clears the lti_helper instance variable when you set the current_user' do
|
||||
expect(variable_expander.lti_helper).not_to be nil
|
||||
|
@ -131,6 +140,16 @@ module Lti
|
|||
end
|
||||
end
|
||||
|
||||
describe '#self.default_name_expansions' do
|
||||
let(:expected_keys) do
|
||||
VariableExpander.expansions.values.select { |v| v.default_name.present? }.map(&:name)
|
||||
end
|
||||
|
||||
it 'includes all expansion keys that have default names' do
|
||||
expect(VariableExpander.default_name_expansions).to eq expected_keys
|
||||
end
|
||||
end
|
||||
|
||||
describe '#enabled_capability_params' do
|
||||
let(:enabled_capability) {
|
||||
%w(TestCapability.Foo
|
||||
|
|
Loading…
Reference in New Issue