diff --git a/app/controllers/lti/message_controller.rb b/app/controllers/lti/message_controller.rb index eb408b1dd72..4ef7ff725d4 100644 --- a/app/controllers/lti/message_controller.rb +++ b/app/controllers/lti/message_controller.rb @@ -88,27 +88,32 @@ module Lti oauth_consumer_key: tool_proxy.guid, lti_version: IMS::LTI::Models::LTIModel::LTI_VERSION_2P0, resource_link_id: build_resource_link_id(message_handler), - context_id: Lti::Asset.opaque_identifier_for(@context), - roles: Lti::SubstitutionsHelper.new(@context, @domain_root_account, @current_user).all_roles('lis2'), + context_id: Lti::Asset.opaque_identifier_for(@context) } - launch_params.merge! enabled_parameters(tool_proxy) + if params[:secure_params].present? secure_params = Canvas::Security.decode_jwt(params[:secure_params]) launch_params.merge!({ext_lti_assignment_id: secure_params[:lti_assignment_id]}) if secure_params[:lti_assignment_id].present? end + + @lti_launch = Launch.new + 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)) + launch_params.merge! enabled_parameters(tool_proxy, message_handler, variable_expander) + message = IMS::LTI::Models::Messages::BasicLTILaunchRequest.new(launch_params) message.user_id = Lti::Asset.opaque_identifier_for(@current_user) if @current_user @active_tab = message_handler.asset_string - @lti_launch = Launch.new @lti_launch.resource_url = message.launch_url @lti_launch.link_text = resource_handler.name @lti_launch.launch_type = message.launch_presentation_document_target - tag = find_tag module_sequence(tag) if tag - custom_param_opts = prep_tool_settings(message_handler.parameters, tool_proxy, message.resource_link_id) - custom_param_opts[:content_tag] = tag if tag - message.add_custom_params(custom_params(message_handler.parameters, custom_param_opts.merge(tool: tool_proxy))) + + message.add_custom_params(custom_params(message_handler.parameters, variable_expander)) message.add_custom_params(ToolSetting.custom_settings(tool_proxy.id, @context, message.resource_link_id)) @lti_launch.params = message.signed_post_params(tool_proxy.shared_secret) @@ -133,10 +138,11 @@ module Lti private - def enabled_parameters(tp) - param_capabilities_hash = CapabilitiesHelper.new(@context).parameter_capabilities_hash - enabled_capabilities = IMS::LTI::Models::ToolProxy.from_json(tp.raw_data).enabled_capabilities - param_capabilities_hash.each_with_object({}) { |(k, v), hash| hash[k] = v if enabled_capabilities.include? k } + def enabled_parameters(tp, mh, variable_expander) + tool_proxy = IMS::LTI::Models::ToolProxy.from_json(tp.raw_data) + enabled_capability = tool_proxy.enabled_capabilities + enabled_capability = enabled_capability.concat(mh.capabilities).uniq if mh.capabilities.present? + CapabilitiesHelper.capability_params_hash(enabled_capability, variable_expander) end def module_sequence(tag) @@ -155,9 +161,9 @@ module Lti end end - def custom_params(parameters, opts = {}) + def custom_params(parameters, variable_expander) params = IMS::LTI::Models::Parameter.from_json(parameters || []) - IMS::LTI::Models::Parameter.process_params(params, create_variable_expander(opts)) + IMS::LTI::Models::Parameter.process_params(params, variable_expander) end def find_binding(tool_proxy) diff --git a/app/models/lti/tool_consumer_profile_creator.rb b/app/models/lti/tool_consumer_profile_creator.rb index 4129b25888a..9e4de703f69 100644 --- a/app/models/lti/tool_consumer_profile_creator.rb +++ b/app/models/lti/tool_consumer_profile_creator.rb @@ -9,7 +9,6 @@ module Lti CAPABILITIES = %w( basic-lti-launch-request - User.id Canvas.api.domain LtiLink.custom.url ToolProxyBinding.custom.url @@ -21,18 +20,11 @@ module Lti Canvas.placements.postGrades Canvas.placements.similarityDetection User.username - Person.email.primary vnd.Canvas.Person.email.sis - Person.name.given - Person.name.family - Person.name.full - CourseSection.sourcedId - Person.sourcedId - Membership.role ToolConsumerProfile.url Security.splitSecret Context.id - ).concat(CapabilitiesHelper.new(@context).parameter_capabilities).freeze + ).concat(CapabilitiesHelper.supported_capabilities).freeze RESTRICTED_CAPABILITIES = %W( #{ORIGINALITY_REPORT_SERVICE}.url diff --git a/doc/api/tools_variable_substitutions.md b/doc/api/tools_variable_substitutions.md index 2e9f33692ec..c4f9c1b1495 100644 --- a/doc/api/tools_variable_substitutions.md +++ b/doc/api/tools_variable_substitutions.md @@ -26,6 +26,9 @@ masquerading users. Additionally, when we don't provide enough information or c directly through LTI, tools can request everything they need to use the Canvas API for an even richer experience. +Some substitutions may be used as 'enabled_capabilities' for LTI2 tools. These substitutions have a +'Launch Parameter' label indicating the parameter name that will be sent in the tool launch if enabled. + For more information on variable substitution, see the IMS LTI specification. # Usage/Configuration @@ -96,6 +99,33 @@ particular placement: ``` # Supported Substitutions +## ToolConsumerInstance.guid +returns a unique identifier for the Tool Consumer (Canvas) + +Launch Parameter: *tool_consumer_instance_guid* + +``` +0dWtgJjjFWRNT41WdQMvrleejGgv7AynCVm3lmZ2:canvas-lms +``` + +## Message.locale +returns the current locale + +Launch Parameter: *launch_presentation_locale* + +``` +de +``` + +## Message.documentTarget +communicates the kind of browser window/frame where the Canvas has launched a tool + +Launch Parameter: *launch_presentation_document_target* + +``` +iframe +``` + ## Canvas.api.domain returns the canvas domain for the current context. Should always be available. @@ -104,7 +134,7 @@ canvas.instructure.com ``` ## Canvas.api.collaborationMembers.url -returns the api url for the members of the collaboration. +returns the api url for the members of the collaboration. ``` https://canvas.instructure.com/api/v1/collaborations/1/members @@ -118,7 +148,7 @@ https://canvas.instructure.com ``` ## ToolProxyBinding.memberships.url -returns the URL for the membership service associated with the current context. +returns the URL for the membership service associated with the current context. ``` https://canvas.instructure.com/api/lti/courses/1/membership_service @@ -238,6 +268,8 @@ YYY-MM-DD HH:MM:SS -0700 returns the current course sis source id. Only available when launched in a course. to return the section source id use Canvas.course.sectionIds +Launch Parameter: *lis_course_section_sourcedid* + ``` 1234 ``` @@ -280,23 +312,35 @@ Only available when launched in a course that was copied (excludes cartridge imp ## Person.name.full Returns the full name of the launching user. Only available when launched by a logged in user. + +Launch Parameter: *lis_person_name_full* + ``` John Doe ``` ## Person.name.family Returns the last name of the launching user. Only available when launched by a logged in user. + +Launch Parameter: *lis_person_name_family* + ``` Doe ``` ## Person.name.given Returns the last name of the launching user. Only available when launched by a logged in user. + +Launch Parameter: *lis_person_name_given* + ``` John ``` ## Person.email.primary Returns the primary email of the launching user. Only available when launched by a logged in user. + +Launch Parameter: *lis_person_contact_email_primary* + ``` john.doe@example.com ``` @@ -316,12 +360,18 @@ America/Denver ## User.image Returns the profile picture URL of the launching user. Only available when launched by a logged in user. + +Launch Parameter: *user_image* + ``` https://example.com/picture.jpg ``` ## User.id [duplicates Canvas.user.id and Canvas.user.loginId] Returns the Canvas user_id of the launching user. Only available when launched by a logged in user. + +Launch Parameter: *user_id* + ``` 420000000000042 ``` @@ -339,7 +389,7 @@ false ``` ## Canvas.group.contextIds -returns the context ids for the groups the user belongs to in the course. +returns the context ids for the groups the user belongs to in the course. ``` 1c16f0de65a080803785ecb3097da99872616f0d,d4d8d6ae1611e2c7581ce1b2f5c58019d928b79d,... @@ -347,6 +397,9 @@ returns the context ids for the groups the user belongs to in the course. ## Membership.role Returns the IMS LTI membership service roles for filtering via query parameters. Only available when launched by a logged in user. + +Launch Parameter: *roles* + ``` http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator ``` @@ -579,4 +632,3 @@ Only available when an attachment is present and has usage rights defined. ## Canvas.file.usageRights.copyrightText Only available when an attachment is present and has usage rights defined. - diff --git a/lib/lti/capabilities_helper.rb b/lib/lti/capabilities_helper.rb index b466efcb5e5..6ac0d6d79af 100644 --- a/lib/lti/capabilities_helper.rb +++ b/lib/lti/capabilities_helper.rb @@ -1,40 +1,29 @@ module Lti class CapabilitiesHelper - attr_accessor :context - def initialize(context) - @context = context + 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 + Membership.role).freeze + + def self.supported_capabilities + SUPPORTED_CAPABILITIES end - def parameter_capabilities_hash - @_param_capabilities_hash ||= begin - recommended_params.merge optional_params - end + def self.filter_capabilities(enabled_capability) + enabled_capability & SUPPORTED_CAPABILITIES end - def parameter_capabilities - parameter_capabilities_hash.keys + def self.capability_params_hash(enabled_capability, variable_expander) + variable_expander.enabled_capability_params(filter_capabilities(enabled_capability)) end - - def recommended_params - { - 'launch_presentation_document_target' => IMS::LTI::Models::Messages::Message::LAUNCH_TARGET_IFRAME, - 'tool_consumer_instance_guid' => tc_instance_guid - } - end - - def optional_params - { - 'launch_presentation_locale' => I18n.locale || I18n.default_locale.to_s - } - end - - private - - def tc_instance_guid - if context.respond_to?(:root_account) && context.root_account.present? - context.root_account.lti_guid - end - end - end end diff --git a/lib/lti/variable_expander.rb b/lib/lti/variable_expander.rb index b1a59c7377b..10ba5bcd654 100644 --- a/lib/lti/variable_expander.rb +++ b/lib/lti/variable_expander.rb @@ -93,6 +93,44 @@ module Lti end end + def enabled_capability_params(enabled_capabilities) + enabled_capabilities.each_with_object({}) do |capability, hash| + if (expansion = capability.respond_to?(:to_sym) && self.class.expansions["$#{capability}".to_sym]) + hash[expansion.default_name] = expansion.expand(self) if expansion.default_name.present? + end + end + end + + # communicates the kind of browser window/frame where the Canvas has launched a tool + # associated launch param name: launch_presentation_document_target + # @example + # ``` + # ifame + # ``` + register_expansion 'Message.documentTarget', [], + -> { IMS::LTI::Models::Messages::Message::LAUNCH_TARGET_IFRAME }, + default_name: 'launch_presentation_document_target' + + # returns the current locale + # associated launch param name: launch_presentation_locale + # @example + # ``` + # de + # ``` + register_expansion 'Message.locale', [], + -> { I18n.locale || I18n.default_locale }, + default_name: 'launch_presentation_locale' + + # returns a unique identifier for the Tool Consumer (Canvas) + # associated launch param name: 'tool_consumer_instance_guid' + # @example + # ``` + # 0dWtgJjjFWRNT41WdQMvrleejGgv7AynCVm3lmZ2:canvas-lms + # ``` + register_expansion 'ToolConsumerInstance.guid', [], + -> { @root_account.lti_guid }, + default_name: 'tool_consumer_instance_guid' + # returns the canvas domain for the current context. # @example # ``` @@ -282,13 +320,15 @@ module Lti # returns the current course sis source id # to return the section source id use Canvas.course.sectionIds + # associated launch param name: 'lis_course_section_sourcedid' # @example # ``` # 1234 # ``` register_expansion 'CourseSection.sourcedId', [], -> { @context.sis_source_id }, - COURSE_GUARD + COURSE_GUARD, + default_name: 'lis_course_section_sourcedid' # returns the current course enrollment state # @example @@ -338,40 +378,48 @@ module Lti COURSE_GUARD # Returns the full name of the launching user. Only available when launched by a logged in user. + # associated launch param name: lis_person_name_full # @example # ``` # John Doe # ``` register_expansion 'Person.name.full', [], -> { @current_user.name }, - USER_GUARD + USER_GUARD, + default_name: 'lis_person_name_full' # Returns the last name of the launching user. Only available when launched by a logged in user. + # associated launch param name: 'lis_person_name_family' # @example # ``` # Doe # ``` register_expansion 'Person.name.family', [], -> { @current_user.last_name }, - USER_GUARD + USER_GUARD, + default_name: 'lis_person_name_family' # Returns the first name of the launching user. Only available when launched by a logged in user. + # associated launch param name: 'lis_person_name_given' # @example # ``` # John # ``` register_expansion 'Person.name.given', [], -> { @current_user.first_name }, - USER_GUARD + USER_GUARD, + default_name: 'lis_person_name_given' # Returns the primary email of the launching user. Only available when launched by a logged in user. + # associated launch param name: 'lis_person_contact_email_primary' # @example # ``` # john.doe@example.com # ``` register_expansion 'Person.email.primary', [], -> { @current_user.email }, - USER_GUARD + USER_GUARD, + default_name: 'lis_person_contact_email_primary' # Returns the institution assigned email of the launching user. Only available when launched by a logged in user that was added via SIS. @@ -395,22 +443,26 @@ module Lti USER_GUARD # Returns the profile picture URL of the launching user. Only available when launched by a logged in user. + # associated launch param name: 'user_image' # @example # ``` # https://example.com/picture.jpg # ``` register_expansion 'User.image', [], -> { @current_user.avatar_url }, - USER_GUARD + USER_GUARD, + default_name: 'user_image' # Returns the Canvas user_id of the launching user. Only available when launched by a logged in user. + # associated launch param name: 'user_id' # @example # ``` # 420000000000042 # ``` register_expansion 'User.id', [], -> { @current_user.id }, - USER_GUARD + USER_GUARD, + default_name: 'user_id' # Returns the Canvas user_id of the launching user. Only available when launched by a logged in user. # @example @@ -443,13 +495,15 @@ module Lti # Returns the IMS LTI membership service roles for filtering via query parameters. # Only available when launched by a logged in user. + # associated launch param name: 'roles' # @example # ``` # http://purl.imsglobal.org/vocab/lis/v2/institution/person#Administrator # ``` register_expansion 'Membership.role', [], -> { lti_helper.all_roles('lis2') }, - USER_GUARD + USER_GUARD, + default_name: 'roles' # Returns list of LIS role full URNs. # Should always be available. @@ -521,7 +575,8 @@ module Lti # ``` register_expansion 'Person.sourcedId', [], -> { sis_pseudonym.sis_user_id }, - PSEUDONYM_GUARD + PSEUDONYM_GUARD, + default_name: 'lis_person_sourcedid' # Returns the logout service url for the user. # This is the pseudonym the user is actually logged in as. diff --git a/lib/lti/variable_expansion.rb b/lib/lti/variable_expansion.rb index cfee019ed2b..d9f384e6e78 100644 --- a/lib/lti/variable_expansion.rb +++ b/lib/lti/variable_expansion.rb @@ -22,14 +22,15 @@ module Lti class VariableExpansion - attr_reader :name, :permission_groups + attr_reader :name, :permission_groups, :default_name - def initialize(name, permission_groups, expansion_proc, *guards) + def initialize(name, permission_groups, expansion_proc, *guards, default_name: nil) @name = name @permission_groups = permission_groups @expansion_proc = expansion_proc @guards = guards @guards << -> { true } if @guards.empty? + @default_name = default_name end def expand(expander) diff --git a/spec/controllers/lti/message_controller_spec.rb b/spec/controllers/lti/message_controller_spec.rb index eef53ab9af0..b403f688c41 100644 --- a/spec/controllers/lti/message_controller_spec.rb +++ b/spec/controllers/lti/message_controller_spec.rb @@ -37,9 +37,10 @@ module Lti ) end let(:enabled_capability) { - %w(tool_consumer_instance_guid - launch_presentation_document_target - launch_presentation_locale) + %w(ToolConsumerInstance.guid + Message.documentTarget + Message.locale + Membership.role) } let(:tool_proxy) do ToolProxy.create( @@ -267,10 +268,12 @@ module Lti end it 'returns the roles' do + course_with_student(account: account, active_all: true) + user_session(@student) get 'basic_lti_launch_request', account_id: account.id, message_handler_id: message_handler.id, params: {tool_launch_context: 'my_custom_context'} params = assigns[:lti_launch].params.with_indifferent_access - expect(params['roles']).to eq ["http://purl.imsglobal.org/vocab/lis/v2/person#None"] + expect(params['roles']).to eq "http://purl.imsglobal.org/vocab/lis/v2/system/person#User" end it 'adds module item substitutions' do diff --git a/spec/lib/lti/capabilities_helper_spec.rb b/spec/lib/lti/capabilities_helper_spec.rb index 7013aeb9ce0..ef6d47da2f4 100644 --- a/spec/lib/lti/capabilities_helper_spec.rb +++ b/spec/lib/lti/capabilities_helper_spec.rb @@ -1,64 +1,109 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') require_dependency "lti/capabilities_helper" +# Get a list of valid capabilities +# Validate capabilities array +# return capability params hash + module Lti describe CapabilitiesHelper do - let(:test_lti_guid){ 'test-lti-guid-1234' } - let(:root_account){ Account.new } - let(:account){ Account.new(root_account: root_account) } - let(:capabilities_helper){ CapabilitiesHelper.new(account) } - let(:recommended_params){ %w(launch_presentation_document_target tool_consumer_instance_guid) } - let(:optional_params){ %w(launch_presentation_locale) } + let(:root_account) { Account.new(lti_guid: 'test-lti-guid') } + let(:account) { Account.new(root_account: root_account) } + let(:course) { Course.new(account: account) } + let(:group_category) { course.group_categories.new(name: 'Category') } + let(:group) { course.groups.new(name: 'Group', group_category: group_category) } + let(:user) { User.new } + let(:assignment) { Assignment.new } + let(:collaboration) do + ExternalToolCollaboration.new( + title: "my collab", + user: user, + url: 'http://www.example.com' + ) + end + let(:substitution_helper) { stub_everything } + let(:right_now) { DateTime.now } + let(:tool) do + m = mock('tool') + m.stubs(:id).returns(1) + m.stubs(:context).returns(root_account) + shard_mock = mock('shard') + shard_mock.stubs(:settings).returns({encription_key: 'abc'}) + m.stubs(:shard).returns(shard_mock) + m.stubs(:opaque_identifier_for).returns("6cd2e0d65bd5aef3b5ee56a64bdcd595e447bc8f") + m + end + let(:controller) do + request_mock = mock('request') + request_mock.stubs(:url).returns('https://localhost') + request_mock.stubs(:host).returns('/my/url') + request_mock.stubs(:scheme).returns('https') + m = mock('controller') + m.stubs(:css_url_for).with(:common).returns('/path/to/common.scss') + m.stubs(:request).returns(request_mock) + m.stubs(:logged_in_user).returns(user) + m.stubs(:named_context_url).returns('url') + m.stubs(:polymorphic_url).returns('url') + view_context_mock = mock('view_context') + view_context_mock.stubs(:stylesheet_path) + .returns(URI.parse(request_mock.url).merge(m.css_url_for(:common)).to_s) + m.stubs(:view_context).returns(view_context_mock) + m + end - describe "#recommended_params" do - it "contains all supported recommended params" do - expect(capabilities_helper.recommended_params.keys).to match_array(recommended_params) - end + let(:variable_expander) { Lti::VariableExpander.new(root_account, account, controller, current_user: user, tool: tool) } - it "gives correct value for launch_presentation_document_target" - - it "gives correct value for tool_consumer_instance_guid" do - root_account.update_attributes(lti_guid: test_lti_guid) - instance_guid = capabilities_helper.recommended_params['tool_consumer_instance_guid'] - expect(instance_guid).to eq root_account.lti_guid - end - - it "gives nil for tool_consumer_instance_guid if context does not have root_account" do - a = Account.new - c_helper = CapabilitiesHelper.new(a) - instance_guid = c_helper.recommended_params[:tool_consumer_instance_guid] - expect(instance_guid).to be_nil + let(:invalid_enabled_caps){ %w(InvalidCap.Foo AnotherInvalid.Bar) } + let(:valid_enabled_caps){ %w(ToolConsumerInstance.guid Membership.role CourseSection.sourcedId) } + let(: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 + Membership.role) + } + describe '#supported_capabilities' do + it 'returns all supported capabilities asociated with launch params' do + expect(CapabilitiesHelper.supported_capabilities).to match_array(supported_capabilities) end end - describe "#optional_params" do - it "contains all supported optional params" do - expect(capabilities_helper.optional_params.keys).to match_array(optional_params) + describe '#filter_capabilities' do + it 'removes invalid capabilities' do + valid_capabilities = CapabilitiesHelper.filter_capabilities(valid_enabled_caps + invalid_enabled_caps) + expect(valid_capabilities).not_to include(*invalid_enabled_caps) end - it "gives correct value for launch_presentation_locale with locale set" do - allow_any_instance_of(I18n).to receive(:locale) { :en } - launch_locale = capabilities_helper.optional_params['launch_presentation_locale'] - expect(launch_locale).to eq I18n.locale - end - - it "gives correct value for launch_presentation_locale with locale not set" do - allow_any_instance_of(I18n).to receive(:locale) { nil } - allow_any_instance_of(I18n).to receive(:default_locale) { :da } - launch_locale = capabilities_helper.optional_params['launch_presentation_locale'] - expect(launch_locale).to eq I18n.default_locale + it 'does not remove valid capabilities' do + valid_capabilities = CapabilitiesHelper.filter_capabilities(valid_enabled_caps + invalid_enabled_caps) + expect(valid_capabilities).to match_array valid_enabled_caps end end - describe "#parameter_capabilities" do - it "returns keys of all optional and recommended params" do - expect(capabilities_helper.parameter_capabilities).to match_array(recommended_params + optional_params) - end - end + describe '#capability_params_hash' do + let(:valid_keys) { %w(tool_consumer_instance_guid roles lis_course_section_sourcedid) } - describe "#paramter_capabilities_hash" do - it "returns all recommended and optional params" do - expect(capabilities_helper.parameter_capabilities_hash.keys).to match_array(recommended_params + optional_params) + it 'does not include a name (key) for invalid capabilities' do + params_hash = CapabilitiesHelper.capability_params_hash(invalid_enabled_caps + valid_enabled_caps, variable_expander) + expect(params_hash.keys).not_to include(*invalid_enabled_caps) + end + + it 'does include a valid name (key) for valid capabilities' do + params_hash = CapabilitiesHelper.capability_params_hash(invalid_enabled_caps + valid_enabled_caps, variable_expander) + expect(params_hash.keys).to include(*valid_keys) + end + + it 'does include a value for each valid capability' do + params_hash = CapabilitiesHelper.capability_params_hash(invalid_enabled_caps + valid_enabled_caps, variable_expander) + expect(params_hash.values.length).to eq valid_keys.length end end end diff --git a/spec/lib/lti/variable_expander_spec.rb b/spec/lib/lti/variable_expander_spec.rb index 421d73b7bca..26932b644da 100644 --- a/spec/lib/lti/variable_expander_spec.rb +++ b/spec/lib/lti/variable_expander_spec.rb @@ -20,7 +20,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper') require_dependency "lti/variable_expander" module Lti describe VariableExpander do - let(:root_account) { Account.new } + let(:root_account) { Account.new(lti_guid: 'test-lti-guid') } let(:account) { Account.new(root_account: root_account) } let(:course) { Course.new(account: account) } let(:group_category) { course.group_categories.new(name: 'Category') } @@ -116,6 +116,102 @@ module Lti expect(expanded[:some_name]).to eq "my variable is buried in here ${tests_expan} can you find it?" end + describe '#enabled_capability_params' do + let(:enabled_capability) { + %w(TestCapability.Foo + 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) + } + + it 'does not use expansions that do not have default names' do + described_class.register_expansion('TestCapability.Foo', ['a'], -> {'test'}) + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).not_to include 'TestCapability.Foo' + end + + it 'does use expansion that have default names' do + described_class.register_expansion('TestCapability.Foo', ['a'], -> { 'test' }, default_name: 'test_capability_foo') + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.values).to include('test') + end + + it 'does use the default name as the key' do + described_class.register_expansion('TestCapability.Foo', ['a'], -> { 'test' }, default_name: 'test_capability_foo') + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded['test_capability_foo']).to eq 'test' + end + + it 'includes ToolConsumerInstance.guid when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded['tool_consumer_instance_guid']).to eq 'test-lti-guid' + end + + it 'includes CourseSection.sourcedId when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'lis_course_section_sourcedid' + end + + it 'includes Membership.role when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'roles' + end + + it 'includes Person.email.primary when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'lis_person_contact_email_primary' + end + + it 'includes Person.name.given when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'lis_person_name_given' + end + + it 'includes Person.name.family when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'lis_person_name_family' + end + + it 'includes Person.name.full when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'lis_person_name_full' + end + + it 'includes Person.sourcedId when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'lis_person_sourcedid' + end + + it 'includes User.id when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'user_id' + end + + it 'includes User.image when in enabled capability' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'user_image' + end + + it 'includes Message.documentTarget' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'launch_presentation_document_target' + end + + it 'includes Message.locale' do + expanded = subject.enabled_capability_params(enabled_capability) + expect(expanded.keys).to include 'launch_presentation_locale' + end + end + context 'lti1' do it 'handles expansion' do described_class.register_expansion('test_expan', ['a'], -> { @context }) @@ -132,7 +228,19 @@ module Lti expect(expanded['some_name']).to eq "my variable is buried in here 42 can you find it?" end end + describe "#variable expansions" do + it 'has substitution for Message.documentTarget' do + exp_hash = {test: '$Message.documentTarget'} + subject.expand_variables!(exp_hash) + expect(exp_hash[:test]).to eq IMS::LTI::Models::Messages::Message::LAUNCH_TARGET_IFRAME + end + + it 'has substitution for Message.locale' do + exp_hash = {test: '$Message.locale'} + subject.expand_variables!(exp_hash) + expect(exp_hash[:test]).to eq I18n.locale + end it 'has substitution for $Canvas.api.domain' do exp_hash = {test: '$Canvas.api.domain'} diff --git a/spec/lib/lti/variable_expansion_spec.rb b/spec/lib/lti/variable_expansion_spec.rb index b91d5877d2a..7cccd2f19c4 100644 --- a/spec/lib/lti/variable_expansion_spec.rb +++ b/spec/lib/lti/variable_expansion_spec.rb @@ -40,6 +40,11 @@ module Lti expect(var_exp.expand(TestExpander.new)).to eq '$test' end + it 'accepts and sets default_name' do + var_exp = described_class.new('test', [], -> { 'test' }, -> { true }, default_name: 'test_name' ) + expect(var_exp.default_name).to eq 'test_name' + end + it 'expands variables' do var_exp = described_class.new('test', [], -> { @one + @two + @three } ) expect(var_exp.expand(TestExpander.new)).to eq 6 diff --git a/spec/models/lti/tool_consumer_profile_creator_spec.rb b/spec/models/lti/tool_consumer_profile_creator_spec.rb index 0c82adf907e..60c1e80eb2d 100644 --- a/spec/models/lti/tool_consumer_profile_creator_spec.rb +++ b/spec/models/lti/tool_consumer_profile_creator_spec.rb @@ -149,16 +149,20 @@ module Lti expect(subject.create(true).capability_offered).to include 'vnd.Canvas.OriginalityReport.url' end - it 'adds the launch_presentation_document_target paramter capability' do - expect(subject.create(true).capability_offered).to include 'launch_presentation_document_target' + it 'adds the Message.documentTarget capability' do + expect(subject.create(true).capability_offered).to include 'Message.documentTarget' end - it 'adds the tool_consumer_instance_guid paramter capability' do - expect(subject.create(true).capability_offered).to include 'tool_consumer_instance_guid' + it 'adds the ToolConsumerInstance.guid capability' do + expect(subject.create(true).capability_offered).to include 'ToolConsumerInstance.guid' end - it 'adds the launch_presentation_locale paramter capability' do - expect(subject.create(true).capability_offered).to include 'launch_presentation_locale' + it 'adds the Message.locale capability' do + expect(subject.create(true).capability_offered).to include 'Message.locale' + end + + it 'adds the Membership.role capability' do + expect(subject.create(true).capability_offered).to include 'Membership.role' end it 'does not add the OriginalityReport capability if developer_key is false' do