Posting grades with React app

SeifYounis
Community Member

Hi. I'm attempting to create a basic React app with an Express server and integrate this app as a third party LTI app in Canvas. I'm currently able to send a grade to the gradebook using the ims-lti library linked here.

Here's a simplified version of my app and what I want to do. In my Express server, I have the following: 

 
require('dotenv').config()

const path = require('path')
const port = process.env.PORT || 3000;

const express = require('express')
const app = express()
const lms = require('./src/assets/lms.js')
 
global.sess = {};

// Used to parse request data that sent from web pages in JSON format
app.use(express.json())
app.use(express.urlencoded({extended: false}))

app.use(express.static(path.join(__dirname, 'build')));
app.get(/[a-z]+/, function (req, res) {
  res.sendFile(path.join(__dirname, 'build', 'index.html'));
});

app.post('/application', function(req, res) {
  var provider = global.sess.provider;

  if(provider.outcome_service) {
    provider.outcome_service.send_replace_result(parseFloat(req.body.score),  
    (err, result) => {
      console.log("Graded")
    })
  }
})

app.post('/launch', lms.handleLaunch);
 
These functions, including handleLaunch, are part of my Canvas integration.
 
const lti = require('ims-lti');
// MemoryStore shouldn't be used in production. Timestamps must be valid within a 5 minute grace period.
const nonceStore = new lti.Stores.MemoryStore();

// secrets should be stored securely in a production app
const secrets = {
  demo: 'xzc342AScx',
  demo2: 'dh43fvL-ew'
};

const getSecret = (consumerKey, callback) => {
  const secret = secrets[consumerKey];
  if (secret) {
    return callback(null, secret);
  }

  let err = new Error(`Unknown consumer ${consumerKey}`);
  err.status = 403;

  return callback(err);
};

exports.handleLaunch = (req, res, next) => {
 if (!req.body) {
    let err = new Error('Expected a body');
    err.status = 400;
    return next(err);
  }

  const consumerKey = req.body.oauth_consumer_key;
  if (!consumerKey) {
    let err = new Error('Expected a consumer');
    err.status = 422;
    return next(err);
  }

  getSecret(consumerKey, (err, consumerSecret) => {
    if (err) {
      return next(err);
    }

    const provider = new lti.Provider(consumerKey, consumerSecret, nonceStore, lti.HMAC_SHA1);

    provider.valid_request(req, req.body, (err, isValid) => {
      if (err) {
        return next(err);
      }

      if (isValid) {
        if(provider.outcome_service) {
          global.sess.provider = provider;

          return res.redirect('/login');
        }
        return res.send(`It looks like this LTI wasn't launched as an assignment, \
        or you are trying to take it as a teacher rather than as a student.`);

      } else {
        return next(err);
      }
    });
  });
};  
 

Here's a link to the Github repository that I based my Canvas integration off of.

 
 

And finally, here is my React page where I allow the user to input a grade.

import React from 'react'
import { withRouter } from 'react-router-dom';

class LoginPage extends React.Component {
    constructor() {
        super();
       
        this.state = {
            score: 0.000
        }
    }

    submitForm(e) {
        e.preventDefault();

        fetch('/application', {
            method: 'POST',
            body: JSON.stringify(this.state),
            headers: {
                'content-Type': 'application/json'
            },
        }).then(function (response) {
            return response.json()
        }).then(function (body) {
            console.log(body);
        });

        this.props.history.push('/testing')
    }

    render() {
        return (
            <body>
                <div>
                    <form onSubmit={this.submitForm.bind(this)}>
                        <input
                            type="number"
                            placeholder="Score"
                            step="0.001"
                            onChange={e => this.setState({ score: e.target.value })}>
                        </input>

                        <button type="submit">Begin</button>
                    </form>
                </div>
            </body>
        )
    }
}

export default withRouter(LoginPage);
 
I want to know if there is a way I can retain the provider data I create in my handleLaunch function without using a global session variable in my app.post('/application') function. I know Node can use request sessions, including storing sessions in a database but I am not sure how to make them work in this case.
 

Any insights on my issue would be greatly appreciated.

0 Likes