class AJIMS::LTI::OutcomeRequest

Class for consuming/generating LTI Outcome Requests

Outcome Request documentation: www.imsglobal.org/lti/v1p1pd/ltiIMGv1p1pd.html#_Toc309649691

This class can be used by both Tool Providers and Tool Consumers. Each will use it a bit differently. The Tool Provider will use it to POST an OAuth-signed request to a TC. A Tool Consumer will use it to parse such a request from a TP.

Tool Provider Usage

An OutcomeRequest will generally be created through a configured ToolProvider object. See the ToolProvider documentation.

Tool Consumer Usage

When an outcome request is sent from a TP the body of the request is XML. This class parses that XML and provides a simple interface for accessing the information in the request. Typical usage would be:

# create an OutcomeRequest from the request object
req = IMS::LTI::OutcomeRequest.from_post_request(request)

# access the source id to identify the user who's grade you'd like to access
req.lis_result_sourcedid

# process the request
if req.replace_request?
  # set a new score for the user
elsif req.read_request?
  # return the score for the user
elsif req.delete_request?
  # clear the score for the user
else
  # return an unsupported OutcomeResponse
end

Constants

DELETE_REQUEST
READ_REQUEST
REPLACE_REQUEST

Attributes

consumer_key[RW]
consumer_secret[RW]
lis_outcome_service_url[RW]
lis_result_sourcedid[RW]
message_identifier[RW]
operation[RW]
outcome_response[RW]
post_request[RW]
score[RW]

Public Class Methods

from_post_request(post_request) click to toggle source

Convenience method for creating a new OutcomeRequest from a request object

req = IMS::LTI::OutcomeRequest.from_post_request(request)
# File lib/ajims/lti/outcome_request.rb, line 58
def self.from_post_request(post_request)
  request = OutcomeRequest.new
  request.process_post_request(post_request)
end
new(opts={}) click to toggle source

Create a new OutcomeRequest

@param opts [Hash] initialization hash

# File lib/ajims/lti/outcome_request.rb, line 49
def initialize(opts={})
  opts.each_pair do |key, val|
    self.send("#{key}=", val) if self.respond_to?("#{key}=")
  end
end

Public Instance Methods

delete_request?() click to toggle source

Check whether this request is a deleteResult request

# File lib/ajims/lti/outcome_request.rb, line 108
def delete_request?
  @operation == DELETE_REQUEST
end
outcome_post_successful?() click to toggle source

Check whether the last outcome POST was successful

# File lib/ajims/lti/outcome_request.rb, line 118
def outcome_post_successful?
  @outcome_response && @outcome_response.success?
end
post_delete_result!() click to toggle source

POSTs a deleteResult to the Tool Consumer

@return [OutcomeResponse] The response from the Tool Consumer

# File lib/ajims/lti/outcome_request.rb, line 89
def post_delete_result!
  @operation = DELETE_REQUEST
  post_outcome_request
end
post_outcome_request() click to toggle source

POST an OAuth signed request to the Tool Consumer

@return [OutcomeResponse] The response from the Tool Consumer

# File lib/ajims/lti/outcome_request.rb, line 125
def post_outcome_request
  raise AJIMS::LTI::InvalidLTIConfigError, "" unless has_required_attributes?

  consumer = OAuth::Consumer.new(@consumer_key, @consumer_secret)
  token = OAuth::AccessToken.new(consumer)

  res = token.post(
    @lis_outcome_service_url,
    generate_request_xml,
    'Content-Type' => 'application/xml'
  )

  @outcome_response = extend_outcome_response(OutcomeResponse.new)
  @outcome_response.process_post_response(res)
end
post_read_result!() click to toggle source

POSTs a readResult to the Tool Consumer

@return [OutcomeResponse] The response from the Tool Consumer

# File lib/ajims/lti/outcome_request.rb, line 97
def post_read_result!
  @operation = READ_REQUEST
  post_outcome_request
end
post_replace_result!(score, submitted_at: nil) click to toggle source

POSTs the given score to the Tool Consumer with a replaceResult

@return [OutcomeResponse] The response from the Tool Consumer

# File lib/ajims/lti/outcome_request.rb, line 78
def post_replace_result!(score, submitted_at: nil)
  @operation = REPLACE_REQUEST
  @score = score
  @submitted_at = submitted_at

  post_outcome_request
end
process_post_request(post_request) click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 63
def process_post_request(post_request)
  self.post_request = post_request
  if post_request.body.respond_to?(:read)
    xml = post_request.body.read
    post_request.body.rewind
  else
    xml = post_request.body
  end
  self.process_xml(xml)
  self
end
process_xml(xml) click to toggle source

Parse Outcome Request data from XML

# File lib/ajims/lti/outcome_request.rb, line 142
def process_xml(xml)
  doc = REXML::Document.new xml
  @message_identifier = doc.text("//imsx_POXRequestHeaderInfo/imsx_messageIdentifier")
  @lis_result_sourcedid = doc.text("//resultRecord/sourcedGUID/sourcedId")

  if REXML::XPath.first(doc, "//deleteResultRequest")
    @operation = DELETE_REQUEST
  elsif REXML::XPath.first(doc, "//readResultRequest")
    @operation = READ_REQUEST
  elsif REXML::XPath.first(doc, "//replaceResultRequest")
    @operation = REPLACE_REQUEST
    @score = doc.get_text("//resultRecord/result/resultScore/textString")
  end
  extention_process_xml(doc)
end
read_request?() click to toggle source

Check whether this request is a readResult request

# File lib/ajims/lti/outcome_request.rb, line 113
def read_request?
  @operation == READ_REQUEST
end
replace_request?() click to toggle source

Check whether this request is a replaceResult request

# File lib/ajims/lti/outcome_request.rb, line 103
def replace_request?
  @operation == REPLACE_REQUEST
end

Private Instance Methods

extention_process_xml(doc) click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 160
def extention_process_xml(doc)
end
generate_request_xml() click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 198
def generate_request_xml
  builder = Builder::XmlMarkup.new #(:indent=>2)
  builder.instruct!

  builder.imsx_POXEnvelopeRequest("xmlns" => "http://www.imsglobal.org/lis/oms1p0/pox") do |env|
    env.imsx_POXHeader do |header|
      header.imsx_POXRequestHeaderInfo do |info|
        info.imsx_version "V1.0"
        info.imsx_messageIdentifier @message_identifier || AJIMS::LTI::generate_identifier
      end
    end
    env.imsx_POXBody do |body|
      body.tag!(@operation + 'Request') do |request|
        request.submissionDetails do |details|
          submission_details(details)
        end

        request.resultRecord do |record|
          record.sourcedGUID do |guid|
            guid.sourcedId @lis_result_sourcedid
          end
          results(record)
        end
      end
    end
  end
end
has_required_attributes?() click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 194
def has_required_attributes?
  @consumer_key && @consumer_secret && @lis_outcome_service_url && @lis_result_sourcedid && @operation
end
has_result_data?() click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 173
def has_result_data?
  !!@score
end
has_result_time?() click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 163
def has_result_time?
  !@submitted_at.nil?
end
result_values(node) click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 185
def result_values(node)
  if @score
    node.resultScore do |res_score|
      res_score.language "en" # 'en' represents the format of the number
      res_score.textString @score.to_s
    end
  end
end
results(node) click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 177
def results(node)
  return unless has_result_data?

  node.result do |res|
    result_values(res)
  end
end
submission_details(node) click to toggle source
# File lib/ajims/lti/outcome_request.rb, line 167
def submission_details(node)
  return unless has_result_time?

  node.submittedAt @submitted_at.to_s
end