Selective Tool Profile Course Copy

fixes PLAT-2634

Test plan:
* Using a course with two lti 2 tools installed
  * Export the course
  * Then selectively import the course into a new course that doesn't
    have the LTI 2 tools installed
  * Ensure that the 2 lti tools are listed in the dialog
  * Ensure that only the tool profiles that are checked leave warnings
    during the import
* Do a selective course copy from the course that has the two lti 2
  tools installed.
  * Ensure all the same stuff as above
* Do selective course export via the api
  * Go through the export process using the following endpoint and
    options
    /api/v1/courses/:course_id/content_exports?export_type=common_cartridge&select[all_tool_profiles]=1
  * Ensure that all the tool profiles in the course are exported
  * Go through the export process using the following endpoint and
    options
    /api/v1/courses/:course_id/content_exports?export_type=common_cartridge&select[tool_profiles][]=<id for a tool profile in the course>
  * Ensure that only the selected tool profile is exported

Change-Id: Ib4cfbad35476369aafd8bf66214ef3efb51850e0
Reviewed-on: https://gerrit.instructure.com/116286
Reviewed-by: James Williams  <jamesw@instructure.com>
Reviewed-by: Nathan Mills <nathanm@instructure.com>
QA-Review: August Thornton <august@instructure.com>
Tested-by: Jenkins
Product-Review: Karl Lloyd <karl@instructure.com>
This commit is contained in:
Andrew Butterfield 2017-06-20 13:20:29 -07:00
parent c665297df2
commit 24c8c5e170
13 changed files with 138 additions and 4 deletions

View File

@ -92,6 +92,7 @@ define [
discussion_topics: "icon-discussion"
wiki_pages: "icon-note-light"
context_external_tools: "icon-lti"
tool_profiles: "icon-lti"
announcements: "icon-announcement"
calendar_events: "icon-calendar-days"
rubrics: "icon-rubric"

View File

@ -26,6 +26,8 @@ module Importers
tool_profiles.each do |tool_profile|
begin
values = tease_out_required_values!(tool_profile)
next unless migration.import_object?('tool_profiles', tool_profile['migration_id'])
tool_proxies = Lti::ToolProxy.find_active_proxies_for_context_by_vendor_code_and_product_code(
context: migration.context,
vendor_code: values[:vendor_code],

View File

@ -27,6 +27,7 @@ module Canvas::Migration::Helpers
['discussion_topics', -> { I18n.t('lib.canvas.migration.discussion_topics', 'Discussion Topics') }],
['wiki_pages', -> { I18n.t('lib.canvas.migration.wikis', 'Wiki Pages') }],
['context_external_tools', -> { I18n.t('lib.canvas.migration.external_tools', 'External Tools') }],
['tool_profiles', -> { I18n.t('lib.canvas.migration.tool_profiles', 'Tool Profiles') }],
['announcements', -> { I18n.t('lib.canvas.migration.announcements', 'Announcements') }],
['calendar_events', -> { I18n.t('lib.canvas.migration.calendar_events', 'Calendar Events') }],
['rubrics', -> { I18n.t('lib.canvas.migration.rubrics', 'Rubrics') }],
@ -234,6 +235,10 @@ module Canvas::Migration::Helpers
source.linked_learning_outcomes.active.select('learning_outcomes.id,short_description').each do |item|
content_list << course_item_hash(type, item)
end
when 'tool_profiles'
source.tool_proxies.active.select("id, name").each do |item|
content_list << course_item_hash(type, item)
end
else
if source.respond_to?(type)
scope = source.send(type).select(:id).except(:preload)
@ -272,6 +277,8 @@ module Canvas::Migration::Helpers
count = source.discussion_topics.active.only_discussion_topics.count
elsif type == 'learning_outcomes'
count = source.linked_learning_outcomes.count
elsif type == 'tool_profiles'
count = source.tool_proxies.active.count
elsif source.respond_to?(type) && source.send(type).respond_to?(:count)
scope = source.send(type).except(:preload)
if scope.klass.respond_to?(:not_deleted)

View File

@ -435,6 +435,19 @@ module MigratorHelper
end
end
if @course[:tool_profiles]
@overview[:tool_profiles] = []
@course[:tool_profiles].each do |tool_profile|
title = tool_profile.dig('tool_profile', 'product_instance', 'product_info', 'product_name', 'default_value')
next unless title
profile = {
migration_id: tool_profile['migration_id'],
title: title
}
@overview[:tool_profiles] << profile
end
end
if @course[:learning_outcomes]
@overview[:learning_outcomes] = []
@course[:learning_outcomes].each do |outcome|

View File

@ -28,6 +28,7 @@ module CC::Importer::Canvas
file_path = File.join @unzipped_file_path, file['href']
json = JSON.parse(File.read(file_path))
json['resource_href'] = file['href']
json['migration_id'] = res['identifier']
tool_profiles << json
end

View File

@ -20,7 +20,12 @@ module CC
module ToolProfiles
def create_tool_profiles
@course.tool_proxies.active.each do |tool_proxy|
next unless export_object?(tool_proxy)
# This is grossness that I added until we have a proper
# ToolProfile ActiveRecord class
tool_proxy.define_singleton_method(:asset_string) do
"tool_profile_#{id}"
end
next unless export_object?(tool_proxy, 'tool_profiles')
migration_id = create_key(tool_proxy)
file_name = "#{migration_id}.json"

View File

@ -98,6 +98,14 @@ define [
equal nameValue, 'copy[all_assignments]', 'Adds the correct name attribute from property'
QUnit.module "#getIconClass",
teardown: -> CheckboxHelper.teardown()
test 'returns lti icon class for tool profiles', ->
CheckboxHelper.renderView()
CheckboxHelper.checkboxView.model.set(type: 'tool_profiles')
equal CheckboxHelper.checkboxView.getIconClass(), 'icon-lti'
QUnit.module "Sublevel Content Checkbox and Carrot Behaviors",
setup: ->
fakeENV.setup()

View File

@ -5,6 +5,7 @@
"registration_url": "https://www.samplelaunch.com/register"
},
"resource_href": "href",
"migration_id": "m_id",
"tool_profile": {
"lti_version": "LTI-2p0",
"product_instance": {

View File

@ -5,6 +5,7 @@
"registration_url": "https://www.samplelaunch.com/register"
},
"resource_href": "href",
"migration_id": "m_id",
"tool_profile": {
"lti_version": "LTI-2p0",
"product_instance": {

View File

@ -16,7 +16,8 @@
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
require File.expand_path(File.dirname(__FILE__) + '/../../../../spec_helper.rb')
require File.expand_path(File.dirname(__FILE__) + '/../../../../lti2_course_spec_helper')
describe Canvas::Migration::Helpers::SelectiveContentFormatter do
context "overview json data" do
@ -26,6 +27,7 @@ describe Canvas::Migration::Helpers::SelectiveContentFormatter do
'modules' => [{'title' => 'a1', 'migration_id' => 'a1'}],
'wikis' => [{'title' => 'a1', 'migration_id' => 'a1'}],
'external_tools' => [{'title' => 'a1', 'migration_id' => 'a1'}],
'tool_profiles' => [{'title' => 'a1', 'migration_id' => 'a1'}],
'outcomes' => [{'title' => 'a1', 'migration_id' => 'a1'}],
'file_map' => {'oi' => {'title' => 'a1', 'migration_id' => 'a1'}},
'assignments' => [{'title' => 'a1', 'migration_id' => 'a1'},{'title' => 'a2', 'migration_id' => 'a2', 'assignment_group_migration_id' => 'a1'}],
@ -56,6 +58,7 @@ describe Canvas::Migration::Helpers::SelectiveContentFormatter do
{:type=>"quizzes", :property=>"copy[all_quizzes]", :title=>"Quizzes", :count=>1, :sub_items_url=>"https://example.com?type=quizzes"},
{:type=>"wiki_pages", :property=>"copy[all_wiki_pages]", :title=>"Wiki Pages", :count=>1, :sub_items_url=>"https://example.com?type=wiki_pages"},
{:type=>"context_external_tools", :property=>"copy[all_context_external_tools]", :title=>"External Tools", :count=>1, :sub_items_url=>"https://example.com?type=context_external_tools"},
{:type=>"tool_profiles", :property=>"copy[all_tool_profiles]", :title=>"Tool Profiles", :count=>1, :sub_items_url=>"https://example.com?type=tool_profiles"},
{:type=>"learning_outcomes", :property=>"copy[all_learning_outcomes]", :title=>"Learning Outcomes", :count=>1},
{:type=>"attachments", :property=>"copy[all_attachments]", :title=>"Files", :count=>1, :sub_items_url=>"https://example.com?type=attachments"}]
end
@ -171,10 +174,13 @@ describe Canvas::Migration::Helpers::SelectiveContentFormatter do
end
context "course copy" do
include_context "lti2_course_spec_helper"
let(:formatter) { Canvas::Migration::Helpers::SelectiveContentFormatter.new(@migration) }
before do
course_model
tool_proxy.context = @course
tool_proxy.save!
@topic = @course.discussion_topics.create!(:message => "hi", :title => "discussion title")
@cm = @course.context_modules.create!(:name => "some module")
attachment_model(:context => @course, :filename => 'a5.html')
@ -191,9 +197,10 @@ describe Canvas::Migration::Helpers::SelectiveContentFormatter do
it "should list top-level items" do
#groups should not show up even though there are some
expect(formatter.get_content_list).to eq [{:type=>"course_settings", :property=>"copy[all_course_settings]", :title=>"Course Settings"},
expect(formatter.get_content_list).to match_array [{:type=>"course_settings", :property=>"copy[all_course_settings]", :title=>"Course Settings"},
{:type=>"syllabus_body", :property=>"copy[all_syllabus_body]", :title=>"Syllabus Body"},
{:type=>"context_modules", :property=>"copy[all_context_modules]", :title=>"Modules", :count=>1},
{:type=>"tool_profiles", :property=>"copy[all_tool_profiles]", :title=>"Tool Profiles", :count=>1},
{:type=>"discussion_topics", :property=>"copy[all_discussion_topics]", :title=>"Discussion Topics", :count=>1},
{:type=>"wiki_pages", :property=>"copy[all_wiki_pages]", :title=>"Wiki Pages", :count=>1},
{:type=>"announcements", :property=>"copy[all_announcements]", :title=>"Announcements", :count=>1},
@ -236,6 +243,7 @@ describe Canvas::Migration::Helpers::SelectiveContentFormatter do
@topic.destroy
@course_outcome.destroy
@account_outcome.destroy
tool_proxy.destroy
@course.require_assignment_group
@course.assignments.create!.destroy

View File

@ -0,0 +1,78 @@
#
# 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.rb')
describe Canvas::Migration::MigratorHelper do
subject do
Class.new do
include Canvas::Migration::MigratorHelper
attr_accessor :course
end
end
describe "#overview" do
context "tool profiles" do
let(:course) do
{
tool_profiles: [
{
'tool_profile' => {
'product_instance' => {
'product_info' => {
'product_name' => {
'default_value' => 'Test Tool'
}
}
}
},
'migration_id' => 'm_id'
}
]
}
end
it 'returns nothing if there are no tool_profiles' do
helper = subject.new
helper.course = {}
helper.overview()
expect(helper.overview[:tool_profiles]).to be_nil
end
it 'returns a tool profile overview if there is a tool_profile' do
helper = subject.new
helper.course = course
helper.overview()
expect(helper.overview[:tool_profiles]).to match_array [
{
title: 'Test Tool',
migration_id: 'm_id'
}
]
end
it 'returns nothing if the tool_profile data is misconfigured' do
helper = subject.new
course[:tool_profiles].first['tool_profile']['product_instance'] = {}
helper.course = course
helper.overview()
expect(helper.overview[:tool_profiles]).to match_array []
end
end
end
end

View File

@ -125,6 +125,7 @@ describe CC::Importer::Canvas::ToolProfileConverter do
"meta" => {
"registration_url" => "https://register.me/register"
},
"migration_id" => "i964fd8107ac2c2e75e9a142971693976",
"resource_href" => "i964fd8107ac2c2e75e9a142971693976.json"
})
end

View File

@ -64,11 +64,18 @@ describe Importers::ToolProfileImporter do
let(:data) { get_import_data('', 'nonmatching_tool_profiles') }
let(:migration) { double(context: course) }
it 'does nothing' do
it 'adds a warning to the migration about finding a different version' do
tool_proxy # necessary to instantiate tool_proxy
allow(migration).to receive(:import_object?).with(any_args).and_return(true)
expect(migration).to receive(:add_warning).with("We found a different version of \"learn abc's\" installed for your course. If this tool fails to work as intended, try reregistering or reinstalling it.")
Importers::ToolProfileImporter.process_migration(data, migration)
end
it 'does nothing' do
tool_proxy # necessary to instantiate tool_proxy
allow(migration).to receive(:import_object?).with(any_args).and_return(false)
expect { Importers::ToolProfileImporter.process_migration(data, migration) }.not_to raise_error
end
end
context 'with tool profile and matching tool proxies' do
@ -79,6 +86,7 @@ describe Importers::ToolProfileImporter do
it 'does nothing' do
tool_proxy # necessary to instantiate tool_proxy
allow(migration).to receive(:import_object?).with(any_args).and_return(true)
expect { Importers::ToolProfileImporter.process_migration(data, migration) }.not_to raise_error
end
end