Posting grades with React app
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
09-30-2021
02:08 PM
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.