From aee61e2bcbb6af9da6be1fb068882d0f0f0a132a Mon Sep 17 00:00:00 2001 From: David Wagler Date: Fri, 22 Sep 2017 08:19:22 -0400 Subject: [PATCH 1/4] Update to use let and const where applicable, clean up the requires and documentation --- README.md | 2 +- docs/authentication.md | 28 +++++------ docs/configurations.md | 2 +- package.json | 3 +- server-local.js | 11 ++-- server.js | 25 +++++----- src/apis/content.js | 69 +++++++++++++------------- src/apis/grades.js | 42 ++++++++-------- src/apis/profileimage.js | 20 ++++---- src/apis/whoami.js | 22 ++++---- src/authorization/idkeyauth.js | 12 ++--- src/authorization/oauth.js | 17 +++---- src/helpers.js | 12 ++--- src/remote-plugins/courseimport-cim.js | 14 +++--- src/remote-plugins/isf-cim.js | 14 +++--- src/remote-plugins/quicklink-cim.js | 14 +++--- 16 files changed, 147 insertions(+), 160 deletions(-) diff --git a/README.md b/README.md index def9cc0..18ab1bc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Out of the box this solution was built to work with the Devcop Brightspace insta ``` 5. Now that the required Node packages are installed the local node server can be started by running: ```shell - npm run start + npm run local ``` 6. The server should now be up and running locally and in a browser you can navigate to: https://localhost:3434 diff --git a/docs/authentication.md b/docs/authentication.md index f48136c..07b1568 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -13,25 +13,25 @@ The code for the ID/Key Authentication can be found in the [idkeyauth.js](../src ``` * Once the SDK is imported we can create application context using the Instance URL, the Application Key and the Application Id. The Application Key and Application Id are received when an application is registered in Brightspace using the 'Manage Extensibility' tool. The code for creating this context is: ```javascript - var appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey); + const appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey); ``` * The ```/idkeyauth``` route exists in the project to initiate the ID/Key Authentication protocol using the created application context. When this route is navigated to in the browser the user is redirected to the Learning Environment where they are pompted to accept the application's ability to make APIs on their behalf. You can see in the callback that we call the [```createUserContext```](https://github.com/Brightspace/valence-sdk-javascript/blob/master/lib/valence.js#L266) which grabs userId and userKey from the query parameters returned from Brightspace. * Once they have accepted the terms the user is redirected to the ```/idkeycallback``` route where the received userKey and userId are stored in a cookie so that subsequent requests can be signed using this context. The follwing code is how the context is setup again and used: ```javascript // Grab the UserId and UserKey from the cookie. - var userId = req.cookies[configs.cookieName].userId; - var userKey = req.cookies[configs.cookieName].userKey; + const userId = req.cookies[configs.cookieName].userId; + const userKey = req.cookies[configs.cookieName].userKey; // Setup user context using the values from the cookie. - var userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); + const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); // Create an authenticated URL using the SDK. - var apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'GET'); + const apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'GET'); ``` ## OAuth 2.0 -The code for the OAuth 2.0 implementation can be found in the [oauth.js](../src/authorization/oauth.js) file. Out of the box there are many supported OAuth 2.0 libraries that you can use in order to make your authenticated requests and support you through the authentication workflow. One thing to keep in mind is that OAuth 2.0 requires the calling application to be granted ```scopes``` that represent what routes the OAuth client can execute. +The code for the OAuth 2.0 implementation can be found in the [oauth.js](../src/authorization/oauth.js) file. Out of the box there are many supported OAuth 2.0 libraries that you can use in order to make your authenticated requests and support you through the authentication workflow. One thing to keep in mind is that OAuth 2.0 requires the calling application to be granted ```scopes``` that represent what routes the OAuth client is authorized to access. Currently for the samples the following scopes: * ```core:*:*``` @@ -41,9 +41,9 @@ The following is the workflow the sample has implemented: * The first order of business is to attain an authorization code from the [Authorization Endpoint](http://docs.valence.desire2learn.com/basic/oauth2.html#setting-up-oauth-2-0-authentication). In order to recieve an auth code there are several configurations that need to be sent as query parameters. The following code illustrates this: ```javascript // Using the imported 'querystring' library, create the query parameter list passing in the required variables. - var authCodeParams = querystring.stringify({ + const authCodeParams = querystring.stringify({ response_type: "code", - redirect_uri: configs.getRedirectUri(req), + redirect_uri: helpers.getRedirectUri(req), client_id: configs.clientId, scope: configs.authCodeScope, state: configs.state @@ -55,10 +55,10 @@ The following is the workflow the sample has implemented: * Once the user has granted the application permission the user is redirected back to the ```/oauthcallback``` route. In the callback the recieved Authorization Code is exchanged for an Access Token by calling the [Token Endpoint](http://docs.valence.desire2learn.com/basic/oauth2.html#setting-up-oauth-2-0-authentication) that can then be used to make API calls. The following code is responsible for this exchange: ```javascript // Retrieve the authorization code from the query parameter. - var authorizationCode = req.query.code; + const authorizationCode = req.query.code; // Verify that the state passed into the request for an Auth code matches the state passed back to the callback. - var state = req.query.state; + const state = req.query.state; if (state !== configs.state) { console.log("The state value from the authorization request was incorrect."); res.status(500).send({ error: "STATE mistmatch - authorization request could not be completed." }); @@ -66,13 +66,13 @@ The following is the workflow the sample has implemented: } // Set the values that will be sent to the Token Endpoint through the body of the request. - var payload = querystring.stringify({ + const payload = querystring.stringify({ grant_type: "authorization_code", redirect_uri: configs.getRedirectUri(req), code: authorizationCode }); - // Using the 'superagent' library with the clientId and ClientSecret sent through the headers as Basic Authorization and the payload sent as the body. + // Using the 'superagent' library with the client_id and client_secret sent through the headers as Basic Authorization and the payload sent as the body. request .post(configs.tokenEndpoint) .auth(configs.clientId, configs.clientSecret) @@ -84,7 +84,7 @@ The following is the workflow the sample has implemented: } else if(response.statusCode != 200) { res.status(response.statusCode).send(response.error); } else { - var accessToken = response.body.access_token; + const accessToken = response.body.access_token; // Save the access token into a cookie to be retrieved later in order to make a request. res.cookie(configs.cookieName, { accessToken: accessToken }, configs.cookieOptions); // Redirect the user back to the index page. @@ -95,7 +95,7 @@ The following is the workflow the sample has implemented: * Now that the Access Token has been saved in the cookie, it can be retrieved later and added as an 'Authorization' header in API requests. The following is an example of this: ```javascript // Retrieve access token from the cookie. - var accessToken = req.cookies[configs.cookieName].accessToken; + const accessToken = req.cookies[configs.cookieName].accessToken; // Set the Authorization header with the access token. request diff --git a/docs/configurations.md b/docs/configurations.md index 1c1f691..b71561d 100644 --- a/docs/configurations.md +++ b/docs/configurations.md @@ -48,7 +48,7 @@ The [index.html]() page has several hardcoded values that indicate to the underl * ```moduleId``` can be updated to the module in content where you would like the new file to be added. * Note: if you are changing the values for the content route be sure to checkout the [content.js](../src/content.js) file in order to update the topic data block to point to the proper content location ('Url' field): ```javascript - var topicData = { + const topicData = { Title: "Sample Word Document Content", ShortTitle: null, Type: 1, diff --git a/package.json b/package.json index c5702c7..96564c2 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,8 @@ "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start": "node server-local.js" + "start": "node server.js", + "local": "node server-local.js" }, "author": "Desire2Learn Inc.", "license": "Apache License 2.0", diff --git a/server-local.js b/server-local.js index b6738c4..4bfb8d9 100644 --- a/server-local.js +++ b/server-local.js @@ -1,10 +1,9 @@ -var https = require('https'); -var selfSigned = require('openssl-self-signed-certificate'); +const https = require('https'), + selfSigned = require('openssl-self-signed-certificate'), + app = require('./server'); -var app = require('./server'); - -var httpsPort = process.env.HTTPS_PORT || 3434; -var options = { +const httpsPort = process.env.HTTPS_PORT || 3434; +const options = { key: selfSigned.key, cert: selfSigned.cert }; diff --git a/server.js b/server.js index 1d638a8..76dc2e7 100644 --- a/server.js +++ b/server.js @@ -1,4 +1,4 @@ -var +const d2l = require('valence'), express = require('express'), request = require('superagent'), @@ -6,29 +6,30 @@ var cookieParser = require('cookie-parser'), configs = require('./src/configurations'), path = require('path'), + helpers = require('./src/helpers'), app = express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(cookieParser()); // Setup the initial D2L context object using the configured instance settings. -var appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey); +const appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey); // Import Authorization -require('./src/authorization/idkeyauth.js')(app, configs, appContext); -require('./src/authorization/oauth.js')(app, request, configs); +require('./src/authorization/idkeyauth.js')(app, configs, appContext, helpers); +require('./src/authorization/oauth.js')(app, request, configs, helpers); // Import Sample API Calls -require('./src/apis/whoami')(app, request, configs, appContext); -require('./src/apis/content')(app, request, configs, appContext); -require('./src/apis/grades')(app, request, configs, appContext); -require('./src/apis/profileimage')(app, request, configs, appContext, __dirname); +require('./src/apis/whoami')(app, request, configs, appContext, helpers); +require('./src/apis/content')(app, request, configs, appContext, helpers); +require('./src/apis/grades')(app, request, configs, appContext, helpers); +require('./src/apis/profileimage')(app, request, configs, appContext, __dirname, helpers); // Import Sample Remote Plugins -require('./src/remote-plugins/isf-cim')(app, request, configs, appContext, path, __dirname); -require('./src/remote-plugins/quicklink-cim')(app, request, configs, appContext, path, __dirname); -require('./src/remote-plugins/courseimport-cim')(app, request, configs, appContext, path, __dirname); -require('./src/remote-plugins/statics.js')(app, express, __dirname); +require('./src/remote-plugins/isf-cim')(app, request, configs, appContext, path, __dirname, helpers); +require('./src/remote-plugins/quicklink-cim')(app, request, configs, appContext, path, __dirname, helpers); +require('./src/remote-plugins/courseimport-cim')(app, request, configs, appContext, path, __dirname, helpers); +require('./src/remote-plugins/statics.js')(app, express, __dirname, helpers); /* GET / * The default server location that will return the index html page. diff --git a/src/apis/content.js b/src/apis/content.js index d64877c..c6841ae 100644 --- a/src/apis/content.js +++ b/src/apis/content.js @@ -1,7 +1,6 @@ -module.exports = function (app, request, configs, appContext) { +const fs = require('fs'); - var fs = require('fs'); - var helpers = require('../helpers'); +module.exports = function (app, request, configs, appContext, helpers) { /* GET /uploadhtmlcontent * Uploads an HTML document to a module within the given course. The OrgUnitId and ModuleId are parameters @@ -9,12 +8,12 @@ module.exports = function (app, request, configs, appContext) { */ app.get('/uploadhtmlcontent', function (req, res) { - var orgUnitId = req.query.orgUnitId; - var moduleId = req.query.moduleId; - var apiPath = '/d2l/api/le/1.22/' + orgUnitId + '/content/modules/' + moduleId + '/structure/'; - var accessToken = req.cookies[configs.cookieName].accessToken; - var boundary = 'xxBOUNDARYxx'; - var topicData = { + const orgUnitId = req.query.orgUnitId; + const moduleId = req.query.moduleId; + const apiPath = '/d2l/api/le/1.22/' + orgUnitId + '/content/modules/' + moduleId + '/structure/'; + const accessToken = req.cookies[configs.cookieName].accessToken; + const boundary = 'xxBOUNDARYxx'; + const topicData = { Title: "Sample HTML Content", ShortTitle: null, Type: 1, @@ -31,11 +30,11 @@ module.exports = function (app, request, configs, appContext) { MajorUpdateText: null, ResetCompletionTracking: null }; - var body = buildMultipartBody(boundary, topicData, './content/file-upload/sample-content.html', 'sample-content.html', 'text/html', 'utf8'); + const body = buildMultipartBody(boundary, topicData, './content/file-upload/sample-content.html', 'sample-content.html', 'text/html', 'utf8'); if (accessToken) { - console.log('Attempting to make the Content Creation route call using OAuth 2.0 authentication.'); - var contentRoute = helpers.createUrl(apiPath, configs); + console.log('Attempting to upload content using OAuth 2.0 authentication.'); + const contentRoute = helpers.createUrl(apiPath, configs); request .post(contentRoute) .set('Authorization', `Bearer ${accessToken}`) @@ -52,11 +51,11 @@ module.exports = function (app, request, configs, appContext) { } }); } else { - console.log('Attempting to make the Content Creation route call using ID Key Authentication.'); - var userId = req.cookies[configs.cookieName].userId; - var userKey = req.cookies[configs.cookieName].userKey; - var userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); - var apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'POST'); + console.log('Attempting to upload content using ID Key Authentication.'); + const userId = req.cookies[configs.cookieName].userId; + const userKey = req.cookies[configs.cookieName].userKey; + const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); + const apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'POST'); request .post(apiCallUrl) .type('multipart/mixed;boundary=' + boundary) @@ -81,12 +80,12 @@ module.exports = function (app, request, configs, appContext) { */ app.get('/uploadworddocument', function (req, res) { - var orgUnitId = req.query.orgUnitId; - var moduleId = req.query.moduleId; - var apiPath = '/d2l/api/le/1.22/' + orgUnitId + '/content/modules/' + moduleId + '/structure/'; - var accessToken = req.cookies[configs.cookieName].accessToken; - var boundary = 'xxBOUNDARYxx'; - var topicData = { + const orgUnitId = req.query.orgUnitId; + const moduleId = req.query.moduleId; + const apiPath = '/d2l/api/le/1.22/' + orgUnitId + '/content/modules/' + moduleId + '/structure/'; + const accessToken = req.cookies[configs.cookieName].accessToken; + const boundary = 'xxBOUNDARYxx'; + const topicData = { Title: "Sample Word Document Content", ShortTitle: null, Type: 1, @@ -104,11 +103,11 @@ module.exports = function (app, request, configs, appContext) { ResetCompletionTracking: null }; - var body = buildMultipartBody(boundary, topicData, './content/file-upload/sample-content.docx', 'sample-content.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); + const body = buildMultipartBody(boundary, topicData, './content/file-upload/sample-content.docx', 'sample-content.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); if (accessToken) { console.log('Attempting to make the Content Creation route call using OAuth 2.0 authentication.') - var contentRoute = helpers.createUrl(apiPath, configs); + const contentRoute = helpers.createUrl(apiPath, configs); request .post(contentRoute) .set('Authorization', `Bearer ${accessToken}`) @@ -128,10 +127,10 @@ module.exports = function (app, request, configs, appContext) { } else { console.log('Attempting to make the Content Creation route call using ID Key Authentication.') - var userId = req.cookies[configs.cookieName].userId; - var userKey = req.cookies[configs.cookieName].userKey; - userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); - var apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'POST'); + const userId = req.cookies[configs.cookieName].userId; + const userKey = req.cookies[configs.cookieName].userKey; + const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); + const apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'POST'); request .post(apiCallUrl) .type('multipart/mixed;boundary=' + boundary) @@ -157,18 +156,18 @@ module.exports = function (app, request, configs, appContext) { * otherwise standard utf8 encoding is used. */ function buildMultipartBody(boundary, jsonData, filePath, fileName, fileContentType, fileEncoding) { - var newLine = '\r\n'; - var doubleDashes = '--'; - var endBoundary = doubleDashes + boundary + doubleDashes + newLine; - var startAndMiddleBoundary = doubleDashes + boundary + newLine; + const newLine = '\r\n'; + const doubleDashes = '--'; + const endBoundary = doubleDashes + boundary + doubleDashes + newLine; + const startAndMiddleBoundary = doubleDashes + boundary + newLine; - var content = startAndMiddleBoundary; + let content = startAndMiddleBoundary; content += 'Content-Type: application/json' + newLine + newLine; content += JSON.stringify(jsonData) + newLine; content += startAndMiddleBoundary; content += 'Content-Disposition: form-data; name=""; filename="' + fileName + '"' + newLine; content += 'Content-Type: ' + fileContentType + newLine + newLine; - var text = ''; + let text = ''; if (!fileEncoding) { text = fs.readFileSync(filePath).toString('base64'); } else { diff --git a/src/apis/grades.js b/src/apis/grades.js index 4920bb1..41060a6 100644 --- a/src/apis/grades.js +++ b/src/apis/grades.js @@ -1,23 +1,22 @@ -module.exports = function (app, request, configs, appContext) { +const async = require('async'); - var helpers = require('../helpers'); - var async = require('async'); +module.exports = function (app, request, configs, appContext, helpers) { /* GET /finalgrades * Returns final grades with user information for all users in the given org unit based on the orgUnitId * passed in through the query parameters. */ app.get('/finalgrades', function (req, res) { - var orgUnitId = req.query.orgUnitId; - var classlistApiPath = '/d2l/api/le/1.12/' + orgUnitId + '/classlist/'; - var accessToken = req.cookies[configs.cookieName].accessToken; - var userId = ''; - var userKey = ''; - var userContext = null; + const orgUnitId = req.query.orgUnitId; + const classlistApiPath = '/d2l/api/le/1.12/' + orgUnitId + '/classlist/'; + const accessToken = req.cookies[configs.cookieName].accessToken; + let userId = ''; + let userKey = ''; + let userContext = null; if (accessToken) { - console.log('Attempting to make the Classlist call using OAuth 2.0 authentication.'); - var classlistRoute = helpers.createUrl(classlistApiPath, configs); + console.log('Attempting to retrieve final grades using OAuth 2.0 authentication.'); + const classlistRoute = helpers.createUrl(classlistApiPath, configs); request .get( classlistRoute ) .set('Authorization', `Bearer ${accessToken}`) @@ -28,16 +27,16 @@ module.exports = function (app, request, configs, appContext) { } else if(response.statusCode != 200) { return res.status(response.statusCode).send(response.error); } else { - var users = JSON.parse(response.text); + const users = JSON.parse(response.text); fetchFinalGrades(orgUnitId, users, accessToken, userContext, res); } }); } else { - console.log('Attempting to make the Classlist call using ID Key Authentication.'); + console.log('Attempting to retrieve final grades using ID Key Authentication.'); userId = req.cookies[configs.cookieName].userId; userKey = req.cookies[configs.cookieName].userKey; userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); - var apiCallUrl = userContext.createAuthenticatedUrl(classlistApiPath, 'GET'); + const apiCallUrl = userContext.createAuthenticatedUrl(classlistApiPath, 'GET'); request .get( apiCallUrl ) .end(function(error, response) { @@ -47,7 +46,7 @@ module.exports = function (app, request, configs, appContext) { } else if(response.statusCode != 200) { res.status(response.statusCode).send(response.error); } else { - var users = JSON.parse(response.text); + const users = JSON.parse(response.text); fetchFinalGrades(orgUnitId, users, accessToken, userContext, res); } }); @@ -57,14 +56,13 @@ module.exports = function (app, request, configs, appContext) { * This function is used to asynchronously call the final grades route for each user. */ function fetchFinalGrades(orgUnitId, users, accessToken, userContext, res) { - var finalGradeBlocks = []; - var gradesApiPath = '/d2l/api/le/1.12/' + orgUnitId + '/grades/final/values/'; - var returnValue = {}; + let finalGradeBlocks = []; + const gradesApiPath = '/d2l/api/le/1.12/' + orgUnitId + '/grades/final/values/'; async.each( users, function(user, callback){ if (accessToken) { - var gradesRoute = helpers.createUrl(gradesApiPath + user.Identifier, configs); + const gradesRoute = helpers.createUrl(gradesApiPath + user.Identifier, configs); request .get( gradesRoute ) .set('Authorization', `Bearer ${accessToken}`) @@ -80,13 +78,13 @@ module.exports = function (app, request, configs, appContext) { callback(response.error); } } else { - var finalGradeBlock = JSON.parse(response.text); + const finalGradeBlock = JSON.parse(response.text); finalGradeBlocks.push({ FinalGrade: finalGradeBlock, User: user }); callback(null); } }); } else { - var gradesRoute = userContext.createAuthenticatedUrl(gradesApiPath + user.Identifier, 'GET'); + const gradesRoute = userContext.createAuthenticatedUrl(gradesApiPath + user.Identifier, 'GET'); request .get( gradesRoute ) .end(function(error, response) { @@ -101,7 +99,7 @@ module.exports = function (app, request, configs, appContext) { callback(response.error); } } else { - var finalGradeBlock = JSON.parse(response.text); + const finalGradeBlock = JSON.parse(response.text); finalGradeBlocks.push({ FinalGrade: finalGradeBlock, User: user }); callback(null); } diff --git a/src/apis/profileimage.js b/src/apis/profileimage.js index ab35169..00d2166 100644 --- a/src/apis/profileimage.js +++ b/src/apis/profileimage.js @@ -1,18 +1,16 @@ -module.exports = function (app, request, configs, appContext, rootDirectory) { - - var helpers = require('../helpers'); +module.exports = function (app, request, configs, appContext, rootDirectory, helpers) { /* GET /uploadprofileimage * Updates the profile image for the user that matches the UserId passed into the route. */ app.get('/uploadprofileimage', function (req, res) { - var userId = req.query.userId; - var apiPath = '/d2l/api/lp/1.9/profile/user/' + userId + '/image'; - var accessToken = req.cookies[configs.cookieName].accessToken; + const userId = req.query.userId; + const apiPath = '/d2l/api/lp/1.9/profile/user/' + userId + '/image'; + const accessToken = req.cookies[configs.cookieName].accessToken; if (accessToken) { console.log('Attempting to upload a user profile image using OAuth 2.0 authentication.'); - var uploadProfileImage = helpers.createUrl(apiPath, configs); + const uploadProfileImage = helpers.createUrl(apiPath, configs); request .post( uploadProfileImage ) .attach('profileImage', rootDirectory + '/content/profile/profileImage.png') @@ -29,10 +27,10 @@ module.exports = function (app, request, configs, appContext, rootDirectory) { }); } else { console.log('Attempting to upload a user profile image using ID Key Authentication.'); - var userId = req.cookies[configs.cookieName].userId; - var userKey = req.cookies[configs.cookieName].userKey; - var userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); - var apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'POST'); + const userId = req.cookies[configs.cookieName].userId; + const userKey = req.cookies[configs.cookieName].userKey; + const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); + const apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'POST'); request .post( apiCallUrl ) .attach('profileImage', rootDirectory + '/content/profile/profileImage.png') diff --git a/src/apis/whoami.js b/src/apis/whoami.js index 2126dad..99ca2e1 100644 --- a/src/apis/whoami.js +++ b/src/apis/whoami.js @@ -1,16 +1,14 @@ -module.exports = function (app, request, configs, appContext) { - - var helpers = require('../helpers'); +module.exports = function (app, request, configs, appContext, helpers) { /* GET /whoami * Returns the who am I information based on the currently authenticated user. */ app.get('/whoami', function (req, res) { - var apiPath = '/d2l/api/lp/1.9/users/whoami'; - var accessToken = req.cookies[configs.cookieName].accessToken; + const apiPath = '/d2l/api/lp/1.9/users/whoami'; + const accessToken = req.cookies[configs.cookieName].accessToken; if (accessToken) { - console.log('Attempting to make the Who Am I call using OAuth 2.0 authentication.'); - var whoamiRoute = helpers.createUrl(apiPath, configs); + console.log('Attempting to make the who am I call using OAuth 2.0 authentication.'); + const whoamiRoute = helpers.createUrl(apiPath, configs); request .get( whoamiRoute ) .set('Authorization', `Bearer ${accessToken}`) @@ -25,11 +23,11 @@ module.exports = function (app, request, configs, appContext) { } }); } else { - console.log('Attempting to make the Who Am I call using ID Key Authentication.'); - var userId = req.cookies[configs.cookieName].userId; - var userKey = req.cookies[configs.cookieName].userKey; - var userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); - var apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'GET'); + console.log('Attempting to make the who am I call using ID Key Authentication.'); + const userId = req.cookies[configs.cookieName].userId; + const userKey = req.cookies[configs.cookieName].userKey; + const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); + const apiCallUrl = userContext.createAuthenticatedUrl(apiPath, 'GET'); request .get( apiCallUrl ) .end(function(error, response) { diff --git a/src/authorization/idkeyauth.js b/src/authorization/idkeyauth.js index 1783f39..6a274ba 100644 --- a/src/authorization/idkeyauth.js +++ b/src/authorization/idkeyauth.js @@ -1,13 +1,11 @@ -module.exports = function (app, configs, appContext) { - - var helpers = require('../helpers'); +module.exports = function (app, configs, appContext, helpers) { /* GET /idkeyauth * This route is used to initiate the ID/Key Authentication workflow. */ app.get('/idkeyauth', function(req, res) { - var callbackTarget = helpers.getIdKeyRedirectUri(req); - var getTokensUrl = appContext.createUrlForAuthentication(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, callbackTarget); + const callbackTarget = helpers.getIdKeyRedirectUri(req); + const getTokensUrl = appContext.createUrlForAuthentication(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, callbackTarget); res.redirect(getTokensUrl); }); @@ -17,8 +15,8 @@ module.exports = function (app, configs, appContext) { * in a cookie so that later requests can be signed using the user's context. */ app.get('/idkeycallback', function(req, res) { - var callbackRoute = req.url; - var userContext = appContext.createUserContext(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, callbackRoute); + const callbackRoute = req.url; + const userContext = appContext.createUserContext(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, callbackRoute); res.cookie(configs.cookieName, { userKey: userContext.userKey, userId: userContext.userId }, configs.cookieOptions); res.redirect('/?authenticationType=idkeyauth'); }); diff --git a/src/authorization/oauth.js b/src/authorization/oauth.js index 3962854..69fa36e 100644 --- a/src/authorization/oauth.js +++ b/src/authorization/oauth.js @@ -1,8 +1,7 @@ -module.exports = function (app, request, configs) { +const querystring = require('querystring'); - var helpers = require('../helpers'), - querystring = require('querystring'); - +module.exports = function (app, request, configs, helpers) { + /* GET /oauth * This endpoint is used to redirect the user to the authentication route * on the learning environment side so that the user can confirm @@ -14,7 +13,7 @@ module.exports = function (app, request, configs) { // The state value is hardcoded for the sample but normally should change with each request to the // authentication endpoint and then stored securely. Please read the configuration.md readme for // more information. - var authCodeParams = querystring.stringify({ + const authCodeParams = querystring.stringify({ response_type: "code", redirect_uri: helpers.getRedirectUri(req), client_id: configs.clientId, @@ -32,14 +31,14 @@ module.exports = function (app, request, configs) { * the token(stores it in a cookie) that can then be used to make API requests. */ app.get('/oauthcallback', function(req, res) { - var authorizationCode = req.query.code; - var state = req.query.state; + const authorizationCode = req.query.code; + const state = req.query.state; if (state !== configs.state) { console.log("The state value from the authorization request was incorrect."); res.status(500).send({ error: "STATE mistmatch - authorization request could not be completed." }); return; } - var payload = querystring.stringify({ + const payload = querystring.stringify({ grant_type: "authorization_code", redirect_uri: helpers.getRedirectUri(req), code: authorizationCode @@ -56,7 +55,7 @@ module.exports = function (app, request, configs) { } else if(response.statusCode != 200) { res.status(response.statusCode).send(response.error); } else { - var accessToken = response.body.access_token; + const accessToken = response.body.access_token; res.cookie(configs.cookieName, { accessToken: accessToken }, configs.cookieOptions); res.redirect('/?authenticationType=oauth'); } diff --git a/src/helpers.js b/src/helpers.js index 9050a5b..c460ee6 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,4 +1,4 @@ -var crypto = require('crypto'); +const crypto = require('crypto'); module.exports = { @@ -19,8 +19,8 @@ module.exports = { * */ generateAuthSignature: function(url, requestBody, secret) { - var signatureBaseString = 'POST&' + encodeURIComponent(url) + '&'; - var first = true; + let signatureBaseString = 'POST&' + encodeURIComponent(url) + '&'; + let first = true; for (const key of Object.keys(requestBody).sort()) { if( key === 'oauth_signature' ){ @@ -43,7 +43,7 @@ module.exports = { .replace(/%40/g, '%2540') .replace(/%5D/g, '%255D'); - var computedSignature = crypto.createHmac('sha1', secret + '&').update(signatureBaseString).digest('base64'); + const computedSignature = crypto.createHmac('sha1', secret + '&').update(signatureBaseString).digest('base64'); return computedSignature; }, @@ -55,7 +55,7 @@ module.exports = { * hash. */ verifyLtiRequest: function(url, requestBody, secret) { - var computedSignature = this.generateAuthSignature(url, requestBody, secret); + const computedSignature = this.generateAuthSignature(url, requestBody, secret); return requestBody.oauth_signature === computedSignature; }, @@ -63,7 +63,7 @@ module.exports = { * Used to generate a unix timestamp used in the signing of LTI responses in the temote plugin examples. */ getUnixTimestamp: function() { - var unix = Math.round(+new Date()/1000); + const unix = Math.round(+new Date()/1000); return unix; }, diff --git a/src/remote-plugins/courseimport-cim.js b/src/remote-plugins/courseimport-cim.js index a876a0d..ccf1637 100644 --- a/src/remote-plugins/courseimport-cim.js +++ b/src/remote-plugins/courseimport-cim.js @@ -1,6 +1,4 @@ -module.exports = function (app, request, configs, appContext, path, directory) { - - var helpers = require('../helpers'); +module.exports = function (app, request, configs, appContext, path, directory, helpers) { /* GET /courseimportselection * Returns the courseimport-cim html page for presentation to the user within Brightspace. @@ -13,7 +11,7 @@ module.exports = function (app, request, configs, appContext, path, directory) { * The LTI endpoint for a Course Import (CIM) remote plugin. */ app.post('/lti/courseimport', function (req, res) { - var url = req.protocol + '://' + req.get('host') + '/lti/courseimport'; + const url = req.protocol + '://' + req.get('host') + '/lti/courseimport'; if (!helpers.verifyLtiRequest(url, req.body, configs.ltiSecret)) { console.log('Could not verify the LTI Request. OAuth 1.0 Validation Failed'); res.status(500).send({error: 'Could not verify the LTI Request. OAuth 1.0 Validation Failed'}); @@ -37,10 +35,10 @@ module.exports = function (app, request, configs, appContext, path, directory) { app.get('/getcourseimportdetails', function (req, res) { // Generate the url to the package based on the user's selection, sent through the query param named // package. - var fileUrl = 'https://github.com/Brightspace/Extensibility-Samples/raw/master/content/importpackage/' + req.query.package; - var contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; + const fileUrl = 'https://github.com/Brightspace/Extensibility-Samples/raw/master/content/importpackage/' + req.query.package; + const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; - var contentItems = { + const contentItems = { "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", "@graph": [ { @@ -53,7 +51,7 @@ module.exports = function (app, request, configs, appContext, path, directory) { ] }; - var responseObject = { + let responseObject = { lti_message_type: 'ContentItemSelection', lti_version: 'LTI-1p0', content_items: JSON.stringify(contentItems), diff --git a/src/remote-plugins/isf-cim.js b/src/remote-plugins/isf-cim.js index 3eea38f..ee08109 100644 --- a/src/remote-plugins/isf-cim.js +++ b/src/remote-plugins/isf-cim.js @@ -1,6 +1,6 @@ -module.exports = function (app, request, configs, appContext, path, directory) { +module.exports = function (app, request, configs, appContext, path, directory, helpers) { + - var helpers = require('../helpers'); /* GET /isfselection * Returns the isf-cim html page for presentation to the user within Brightspace. @@ -13,7 +13,7 @@ module.exports = function (app, request, configs, appContext, path, directory) { * The LTI endpoint for a Insert Stuff (CIM) remote plugin. */ app.post('/lti/isfcontent', function (req, res) { - var url = req.protocol + '://' + req.get('host') + '/lti/isfcontent'; + const url = req.protocol + '://' + req.get('host') + '/lti/isfcontent'; if (!helpers.verifyLtiRequest(url, req.body, configs.ltiSecret)) { console.log('Could not verify the LTI Request. OAuth 1.0 Validation Failed'); res.status(500).send({error: 'Could not verify the LTI Request. OAuth 1.0 Validation Failed'}); @@ -35,10 +35,10 @@ module.exports = function (app, request, configs, appContext, path, directory) { * to Brightspace in order to insert the stuff into Brightspace. */ app.get('/getisfdetails', function (req, res) { - var imageUrl = req.protocol + '://' + req.get('host') + '/content/isf/' + req.query.image; - var contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; + const imageUrl = req.protocol + '://' + req.get('host') + '/content/isf/' + req.query.image; + const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; - var contentItems = { + const contentItems = { "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", "@graph": [ { @@ -57,7 +57,7 @@ module.exports = function (app, request, configs, appContext, path, directory) { ] }; - var responseObject = { + let responseObject = { lti_message_type: 'ContentItemSelection', lti_version: 'LTI-1p0', content_items: JSON.stringify(contentItems), diff --git a/src/remote-plugins/quicklink-cim.js b/src/remote-plugins/quicklink-cim.js index be7d0bf..acc2a88 100644 --- a/src/remote-plugins/quicklink-cim.js +++ b/src/remote-plugins/quicklink-cim.js @@ -1,6 +1,4 @@ -module.exports = function (app, request, configs, appContext, path, directory) { - - var helpers = require('../helpers'); +module.exports = function (app, request, configs, appContext, path, directory, helpers) { /* GET /quicklinkselection * Returns the quicklink-cim html page for presentation to the user within Brightspace. @@ -13,7 +11,7 @@ module.exports = function (app, request, configs, appContext, path, directory) { * The LTI endpoint for a Quicklink (CIM) remote plugin. */ app.post('/lti/quicklinkcontent', function (req, res) { - var url = req.protocol + '://' + req.get('host') + '/lti/quicklinkcontent'; + const url = req.protocol + '://' + req.get('host') + '/lti/quicklinkcontent'; if (!helpers.verifyLtiRequest(url, req.body, configs.ltiSecret)) { console.log('Could not verify the LTI Request. OAuth 1.0 Validation Failed'); res.status(500).send({error: 'Could not verify the LTI Request. OAuth 1.0 Validation Failed'}); @@ -35,10 +33,10 @@ module.exports = function (app, request, configs, appContext, path, directory) { * to Brightspace in order to add the content. */ app.get('/getquicklinkdetails', function (req, res) { - var fileUrl = req.protocol + '://' + req.get('host') + '/content/quicklink/' + req.query.link; - var contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; + const fileUrl = req.protocol + '://' + req.get('host') + '/content/quicklink/' + req.query.link; + const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; - var contentItems = { + const contentItems = { "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", "@graph": [ { @@ -56,7 +54,7 @@ module.exports = function (app, request, configs, appContext, path, directory) { ] }; - var responseObject = { + let responseObject = { lti_message_type: 'ContentItemSelection', lti_version: 'LTI-1p0', content_items: JSON.stringify(contentItems), From a3ff691b1eac51b7701ae8ac4fb6ef7be649af52 Mon Sep 17 00:00:00 2001 From: David Wagler Date: Fri, 22 Sep 2017 08:57:20 -0400 Subject: [PATCH 2/4] Add JSHint to the project and clean up code based on linter feedback --- .jshintrc | 20 +++++++++++++++++ package.json | 6 ++++-- server-local.js | 2 ++ server.js | 2 ++ src/apis/content.js | 30 ++++++++++++++------------ src/apis/grades.js | 22 ++++++++++--------- src/apis/profileimage.js | 10 +++++---- src/apis/whoami.js | 10 +++++---- src/authorization/idkeyauth.js | 4 +++- src/authorization/oauth.js | 12 ++++++----- src/configurations.js | 2 ++ src/helpers.js | 8 ++++--- src/remote-plugins/courseimport-cim.js | 8 ++++--- src/remote-plugins/isf-cim.js | 10 ++++----- src/remote-plugins/quicklink-cim.js | 8 ++++--- src/remote-plugins/statics.js | 6 ++++-- 16 files changed, 104 insertions(+), 56 deletions(-) create mode 100644 .jshintrc diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..d4635a0 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,20 @@ +{ + "node": true, + "bitwise": true, + "camelcase": false, + "curly": true, + "eqeqeq": true, + "immed": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": false, + "strict": true, + "trailing": true, + "smarttabs": false, + "laxcomma": true, + "onevar": false, + "esversion": 6 +} \ No newline at end of file diff --git a/package.json b/package.json index 96564c2..508ec21 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,10 @@ "description": "Sample application showing how to extend Brightspace using APIs, Remote Plugins and other integration points..", "main": "server.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1", + "lint": "jshint src server-local.js server.js", + "local": "node server-local.js", "start": "node server.js", - "local": "node server-local.js" + "test": "echo \"Error: no test specified\" && exit 1" }, "author": "Desire2Learn Inc.", "license": "Apache License 2.0", @@ -20,6 +21,7 @@ "valence": "^1.0.3" }, "devDependencies": { + "jshint": "^2.9.5", "openssl-self-signed-certificate": "^1.1.6" } } diff --git a/server-local.js b/server-local.js index 4bfb8d9..fedcf80 100644 --- a/server-local.js +++ b/server-local.js @@ -1,3 +1,5 @@ +'use strict'; + const https = require('https'), selfSigned = require('openssl-self-signed-certificate'), app = require('./server'); diff --git a/server.js b/server.js index 76dc2e7..20b0d62 100644 --- a/server.js +++ b/server.js @@ -1,3 +1,5 @@ +'use strict'; + const d2l = require('valence'), express = require('express'), diff --git a/src/apis/content.js b/src/apis/content.js index c6841ae..1deb6ee 100644 --- a/src/apis/content.js +++ b/src/apis/content.js @@ -1,3 +1,5 @@ +'use strict'; + const fs = require('fs'); module.exports = function (app, request, configs, appContext, helpers) { @@ -14,11 +16,11 @@ module.exports = function (app, request, configs, appContext, helpers) { const accessToken = req.cookies[configs.cookieName].accessToken; const boundary = 'xxBOUNDARYxx'; const topicData = { - Title: "Sample HTML Content", + Title: 'Sample HTML Content', ShortTitle: null, Type: 1, TopicType: 1, - Url: "/content/enforced/6952-ES100/sample-content.html", + Url: '/content/enforced/6952-ES100/sample-content.html', StartDate: null, EndDate: null, DueDate: null, @@ -42,9 +44,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .send(body) .end(function(error, response) { if (error) { - console.log("Error calling the upload content route", error); + console.log('Error calling the upload content route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); @@ -62,9 +64,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .send(body) .end(function(error, response) { if (error) { - console.log("Error calling the upload content route", error); + console.log('Error calling the upload content route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); @@ -86,11 +88,11 @@ module.exports = function (app, request, configs, appContext, helpers) { const accessToken = req.cookies[configs.cookieName].accessToken; const boundary = 'xxBOUNDARYxx'; const topicData = { - Title: "Sample Word Document Content", + Title: 'Sample Word Document Content', ShortTitle: null, Type: 1, TopicType: 1, - Url: "/content/enforced/6952-ES100/sample-content.docx", + Url: '/content/enforced/6952-ES100/sample-content.docx', StartDate: null, EndDate: null, DueDate: null, @@ -106,7 +108,7 @@ module.exports = function (app, request, configs, appContext, helpers) { const body = buildMultipartBody(boundary, topicData, './content/file-upload/sample-content.docx', 'sample-content.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'); if (accessToken) { - console.log('Attempting to make the Content Creation route call using OAuth 2.0 authentication.') + console.log('Attempting to make the Content Creation route call using OAuth 2.0 authentication.'); const contentRoute = helpers.createUrl(apiPath, configs); request .post(contentRoute) @@ -116,9 +118,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .send(body) .end(function(error, response) { if (error) { - console.log("Error calling the upload content route", error); + console.log('Error calling the upload content route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); @@ -126,7 +128,7 @@ module.exports = function (app, request, configs, appContext, helpers) { }); } else { - console.log('Attempting to make the Content Creation route call using ID Key Authentication.') + console.log('Attempting to make the Content Creation route call using ID Key Authentication.'); const userId = req.cookies[configs.cookieName].userId; const userKey = req.cookies[configs.cookieName].userKey; const userContext = appContext.createUserContextWithValues(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, userId, userKey); @@ -138,9 +140,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .send(body) .end(function(error, response) { if (error) { - console.log("Error calling the upload content route", error); + console.log('Error calling the upload content route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); diff --git a/src/apis/grades.js b/src/apis/grades.js index 41060a6..daba6c1 100644 --- a/src/apis/grades.js +++ b/src/apis/grades.js @@ -1,3 +1,5 @@ +'use strict'; + const async = require('async'); module.exports = function (app, request, configs, appContext, helpers) { @@ -22,9 +24,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .set('Authorization', `Bearer ${accessToken}`) .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); return res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { return res.status(response.statusCode).send(response.error); } else { const users = JSON.parse(response.text); @@ -41,9 +43,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .get( apiCallUrl ) .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { const users = JSON.parse(response.text); @@ -68,10 +70,10 @@ module.exports = function (app, request, configs, appContext, helpers) { .set('Authorization', `Bearer ${accessToken}`) .end(function(error, response) { if (error) { - console.log("Error calling the Final Grades route", error); + console.log('Error calling the Final Grades route', error); callback({message: 'Error calling the Final Grades Route'}); - } else if(response.statusCode != 200) { - if(response.statusCode == 404){ + } else if(response.statusCode !== 200) { + if(response.statusCode === 404){ finalGradeBlocks.push({ FinalGrade: {}, User: user }); callback(null); } else { @@ -89,10 +91,10 @@ module.exports = function (app, request, configs, appContext, helpers) { .get( gradesRoute ) .end(function(error, response) { if (error) { - console.log("Error calling the Final Grades route", error); + console.log('Error calling the Final Grades route', error); callback({message: 'Error calling the Final Grades Route'}); - } else if(response.statusCode != 200) { - if(response.statusCode == 404){ + } else if(response.statusCode !== 200) { + if(response.statusCode === 404){ finalGradeBlocks.push({ FinalGrade: {}, User: user }); callback(null); } else { diff --git a/src/apis/profileimage.js b/src/apis/profileimage.js index 00d2166..51a2a6e 100644 --- a/src/apis/profileimage.js +++ b/src/apis/profileimage.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function (app, request, configs, appContext, rootDirectory, helpers) { /* GET /uploadprofileimage @@ -17,9 +19,9 @@ module.exports = function (app, request, configs, appContext, rootDirectory, hel .set('Authorization', `Bearer ${accessToken}`) .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send({success: true}); @@ -36,9 +38,9 @@ module.exports = function (app, request, configs, appContext, rootDirectory, hel .attach('profileImage', rootDirectory + '/content/profile/profileImage.png') .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send({success: true}); diff --git a/src/apis/whoami.js b/src/apis/whoami.js index 99ca2e1..f9ff247 100644 --- a/src/apis/whoami.js +++ b/src/apis/whoami.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function (app, request, configs, appContext, helpers) { /* GET /whoami @@ -14,9 +16,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .set('Authorization', `Bearer ${accessToken}`) .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); @@ -32,9 +34,9 @@ module.exports = function (app, request, configs, appContext, helpers) { .get( apiCallUrl ) .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); diff --git a/src/authorization/idkeyauth.js b/src/authorization/idkeyauth.js index 6a274ba..3705f5d 100644 --- a/src/authorization/idkeyauth.js +++ b/src/authorization/idkeyauth.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function (app, configs, appContext, helpers) { /* GET /idkeyauth @@ -21,4 +23,4 @@ module.exports = function (app, configs, appContext, helpers) { res.redirect('/?authenticationType=idkeyauth'); }); -} +}; diff --git a/src/authorization/oauth.js b/src/authorization/oauth.js index 69fa36e..c357c67 100644 --- a/src/authorization/oauth.js +++ b/src/authorization/oauth.js @@ -1,3 +1,5 @@ +'use strict'; + const querystring = require('querystring'); module.exports = function (app, request, configs, helpers) { @@ -14,7 +16,7 @@ module.exports = function (app, request, configs, helpers) { // authentication endpoint and then stored securely. Please read the configuration.md readme for // more information. const authCodeParams = querystring.stringify({ - response_type: "code", + response_type: 'code', redirect_uri: helpers.getRedirectUri(req), client_id: configs.clientId, scope: configs.authCodeScope, @@ -34,12 +36,12 @@ module.exports = function (app, request, configs, helpers) { const authorizationCode = req.query.code; const state = req.query.state; if (state !== configs.state) { - console.log("The state value from the authorization request was incorrect."); - res.status(500).send({ error: "STATE mistmatch - authorization request could not be completed." }); + console.log('The state value from the authorization request was incorrect.'); + res.status(500).send({ error: 'STATE mistmatch - authorization request could not be completed.'}); return; } const payload = querystring.stringify({ - grant_type: "authorization_code", + grant_type: 'authorization_code', redirect_uri: helpers.getRedirectUri(req), code: authorizationCode }); @@ -52,7 +54,7 @@ module.exports = function (app, request, configs, helpers) { if (err) { console.log('Access Token Error', err.response || err); res.redirect('/auth'); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { const accessToken = response.body.access_token; diff --git a/src/configurations.js b/src/configurations.js index 7e1658f..b573e51 100644 --- a/src/configurations.js +++ b/src/configurations.js @@ -1,3 +1,5 @@ +'use strict'; + /**** It is not recommended to commit your application key or client secret to ANY repository. In this case these keys were generated against the devcop Brightspace instance which can be used for developer testing. diff --git a/src/helpers.js b/src/helpers.js index c460ee6..475dbfd 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,3 +1,5 @@ +'use strict'; + const crypto = require('crypto'); module.exports = { @@ -7,7 +9,7 @@ module.exports = { * Used when creating URL's for API requests that use the OAuth 2.0 authentication method. */ createUrl: function(apiRoute, configs){ - return configs.instanceScheme + '//' + configs.instanceUrl + ":" + configs.instancePort + apiRoute; + return configs.instanceScheme + '//' + configs.instanceUrl + ':' + configs.instancePort + apiRoute; }, /* function generateAuthSignature @@ -68,10 +70,10 @@ module.exports = { }, getRedirectUri: function(req) { - return req.protocol + "://" + req.headers.host + "/oauthcallback"; + return req.protocol + '://' + req.headers.host + '/oauthcallback'; }, getIdKeyRedirectUri: function(req) { - return req.protocol + "://" + req.headers.host + "/idkeycallback"; + return req.protocol + '://' + req.headers.host + '/idkeycallback'; } }; diff --git a/src/remote-plugins/courseimport-cim.js b/src/remote-plugins/courseimport-cim.js index ccf1637..74dfb8f 100644 --- a/src/remote-plugins/courseimport-cim.js +++ b/src/remote-plugins/courseimport-cim.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function (app, request, configs, appContext, path, directory, helpers) { /* GET /courseimportselection @@ -39,10 +41,10 @@ module.exports = function (app, request, configs, appContext, path, directory, h const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; const contentItems = { - "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", - "@graph": [ + '@context' : 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem', + '@graph': [ { - "@type" : "FileItem", + '@type' : 'FileItem', mediaType: 'application/vnd.d2l.coursepackage1p0', title: req.query.package, text: 'Brightspace sample course package to import.', diff --git a/src/remote-plugins/isf-cim.js b/src/remote-plugins/isf-cim.js index ee08109..c5582cd 100644 --- a/src/remote-plugins/isf-cim.js +++ b/src/remote-plugins/isf-cim.js @@ -1,6 +1,6 @@ -module.exports = function (app, request, configs, appContext, path, directory, helpers) { - +'use strict'; +module.exports = function (app, request, configs, appContext, path, directory, helpers) { /* GET /isfselection * Returns the isf-cim html page for presentation to the user within Brightspace. @@ -39,10 +39,10 @@ module.exports = function (app, request, configs, appContext, path, directory, h const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; const contentItems = { - "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", - "@graph": [ + '@context' : 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem', + '@graph': [ { - "@type" : "ContentItem", + '@type' : 'ContentItem', mediaType: 'image/png', title: 'Brightspace Logo', text: '', diff --git a/src/remote-plugins/quicklink-cim.js b/src/remote-plugins/quicklink-cim.js index acc2a88..711fee6 100644 --- a/src/remote-plugins/quicklink-cim.js +++ b/src/remote-plugins/quicklink-cim.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = function (app, request, configs, appContext, path, directory, helpers) { /* GET /quicklinkselection @@ -37,10 +39,10 @@ module.exports = function (app, request, configs, appContext, path, directory, h const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; const contentItems = { - "@context" : "http://purl.imsglobal.org/ctx/lti/v1/ContentItem", - "@graph": [ + '@context' : 'http://purl.imsglobal.org/ctx/lti/v1/ContentItem', + '@graph': [ { - "@type" : "FileItem", + '@type' : 'FileItem', mediaType: 'text/html', title: req.query.link, text: 'A sample Brightspace Quicklink file.', diff --git a/src/remote-plugins/statics.js b/src/remote-plugins/statics.js index 588e3c2..11a4059 100644 --- a/src/remote-plugins/statics.js +++ b/src/remote-plugins/statics.js @@ -1,5 +1,7 @@ +'use strict'; + module.exports = function (app, express, rootDirectory) { // Setup static folders for serving up content to the Remote Plugins. - app.use("/content/isf", express.static(rootDirectory + '/content/isf')); - app.use("/content/quicklink", express.static(rootDirectory + '/content/quicklink')); + app.use('/content/isf', express.static(rootDirectory + '/content/isf')); + app.use('/content/quicklink', express.static(rootDirectory + '/content/quicklink')); }; \ No newline at end of file From ac8b591236fb1510930302df8c22af33311deac3 Mon Sep 17 00:00:00 2001 From: David Wagler Date: Fri, 22 Sep 2017 10:03:48 -0400 Subject: [PATCH 3/4] Update to use the express router and clean up the requires --- docs/authentication.md | 14 +++++++------- package.json | 2 +- server.js | 22 ++++++++++------------ src/apis/content.js | 26 ++++++++++++++++---------- src/apis/grades.js | 13 ++++++++++--- src/apis/profileimage.js | 13 ++++++++++--- src/apis/whoami.js | 11 +++++++++-- src/authorization/idkeyauth.js | 13 ++++++++++--- src/authorization/oauth.js | 15 +++++++++++---- src/remote-plugins/courseimport-cim.js | 17 +++++++++++++---- src/remote-plugins/isf-cim.js | 16 ++++++++++++---- src/remote-plugins/quicklink-cim.js | 16 ++++++++++++---- src/remote-plugins/statics.js | 4 +++- 13 files changed, 124 insertions(+), 58 deletions(-) diff --git a/docs/authentication.md b/docs/authentication.md index 07b1568..af0f011 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -42,7 +42,7 @@ The following is the workflow the sample has implemented: ```javascript // Using the imported 'querystring' library, create the query parameter list passing in the required variables. const authCodeParams = querystring.stringify({ - response_type: "code", + response_type: 'code', redirect_uri: helpers.getRedirectUri(req), client_id: configs.clientId, scope: configs.authCodeScope, @@ -60,14 +60,14 @@ The following is the workflow the sample has implemented: // Verify that the state passed into the request for an Auth code matches the state passed back to the callback. const state = req.query.state; if (state !== configs.state) { - console.log("The state value from the authorization request was incorrect."); - res.status(500).send({ error: "STATE mistmatch - authorization request could not be completed." }); + console.log('The state value from the authorization request was incorrect.'); + res.status(500).send({ error: 'STATE mistmatch - authorization request could not be completed.' }); return; } // Set the values that will be sent to the Token Endpoint through the body of the request. const payload = querystring.stringify({ - grant_type: "authorization_code", + grant_type: 'authorization_code', redirect_uri: configs.getRedirectUri(req), code: authorizationCode }); @@ -81,7 +81,7 @@ The following is the workflow the sample has implemented: if (err) { console.log('Access Token Error', err.response || err); res.redirect('/auth'); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { const accessToken = response.body.access_token; @@ -103,9 +103,9 @@ The following is the workflow the sample has implemented: .set('Authorization', `Bearer ${accessToken}`) .end(function(error, response) { if (error) { - console.log("Error calling the who am I route", error); + console.log('Error calling the who am I route', error); res.status(500).send({ error: error }); - } else if(response.statusCode != 200) { + } else if(response.statusCode !== 200) { res.status(response.statusCode).send(response.error); } else { res.status(200).send(response.text); diff --git a/package.json b/package.json index 508ec21..09f1d92 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "lint": "jshint src server-local.js server.js", "local": "node server-local.js", "start": "node server.js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "npm run lint" }, "author": "Desire2Learn Inc.", "license": "Apache License 2.0", diff --git a/server.js b/server.js index 20b0d62..1bf256a 100644 --- a/server.js +++ b/server.js @@ -3,12 +3,10 @@ const d2l = require('valence'), express = require('express'), - request = require('superagent'), bodyParser = require('body-parser'), cookieParser = require('cookie-parser'), configs = require('./src/configurations'), path = require('path'), - helpers = require('./src/helpers'), app = express(); app.use(bodyParser.urlencoded({ extended: true })); @@ -18,20 +16,20 @@ app.use(cookieParser()); const appContext = new d2l.ApplicationContext(configs.instanceUrl, configs.applicationId, configs.applicationKey); // Import Authorization -require('./src/authorization/idkeyauth.js')(app, configs, appContext, helpers); -require('./src/authorization/oauth.js')(app, request, configs, helpers); +app.use(require('./src/authorization/idkeyauth.js')(appContext)); +app.use(require('./src/authorization/oauth.js')()); // Import Sample API Calls -require('./src/apis/whoami')(app, request, configs, appContext, helpers); -require('./src/apis/content')(app, request, configs, appContext, helpers); -require('./src/apis/grades')(app, request, configs, appContext, helpers); -require('./src/apis/profileimage')(app, request, configs, appContext, __dirname, helpers); +app.use(require('./src/apis/whoami')(appContext)); +app.use(require('./src/apis/content')(appContext)); +app.use(require('./src/apis/grades')(appContext)); +app.use(require('./src/apis/profileimage')(appContext, __dirname)); // Import Sample Remote Plugins -require('./src/remote-plugins/isf-cim')(app, request, configs, appContext, path, __dirname, helpers); -require('./src/remote-plugins/quicklink-cim')(app, request, configs, appContext, path, __dirname, helpers); -require('./src/remote-plugins/courseimport-cim')(app, request, configs, appContext, path, __dirname, helpers); -require('./src/remote-plugins/statics.js')(app, express, __dirname, helpers); +app.use(require('./src/remote-plugins/isf-cim')(appContext, __dirname)); +app.use(require('./src/remote-plugins/quicklink-cim')(appContext,__dirname)); +app.use(require('./src/remote-plugins/courseimport-cim')(appContext, __dirname)); +require('./src/remote-plugins/statics.js')(app, __dirname); /* GET / * The default server location that will return the index html page. diff --git a/src/apis/content.js b/src/apis/content.js index 1deb6ee..18a5144 100644 --- a/src/apis/content.js +++ b/src/apis/content.js @@ -1,14 +1,19 @@ 'use strict'; -const fs = require('fs'); +const fs = require('fs'), + configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); -module.exports = function (app, request, configs, appContext, helpers) { +module.exports = function (appContext) { /* GET /uploadhtmlcontent * Uploads an HTML document to a module within the given course. The OrgUnitId and ModuleId are parameters * passed in through the query parameters. */ - app.get('/uploadhtmlcontent', function (req, res) { + router.get('/uploadhtmlcontent', function (req, res) { const orgUnitId = req.query.orgUnitId; const moduleId = req.query.moduleId; @@ -80,7 +85,7 @@ module.exports = function (app, request, configs, appContext, helpers) { * Uploads a Word document to a module within the given course. The OrgUnitId and ModuleId are parameters * passed in through the query parameters. */ - app.get('/uploadworddocument', function (req, res) { + router.get('/uploadworddocument', function (req, res) { const orgUnitId = req.query.orgUnitId; const moduleId = req.query.moduleId; @@ -169,14 +174,15 @@ module.exports = function (app, request, configs, appContext, helpers) { content += startAndMiddleBoundary; content += 'Content-Disposition: form-data; name=""; filename="' + fileName + '"' + newLine; content += 'Content-Type: ' + fileContentType + newLine + newLine; - let text = ''; - if (!fileEncoding) { - text = fs.readFileSync(filePath).toString('base64'); - } else { - text = fs.readFileSync(filePath,'utf8'); - } + + const text = fileEncoding ? + fs.readFileSync(filePath,'base64') : + fs.readFileSync(filePath).toString('utf8'); + content += text + newLine; content += endBoundary; return content; } + + return router; }; diff --git a/src/apis/grades.js b/src/apis/grades.js index daba6c1..8de512b 100644 --- a/src/apis/grades.js +++ b/src/apis/grades.js @@ -1,14 +1,19 @@ 'use strict'; -const async = require('async'); +const async = require('async'), + configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); -module.exports = function (app, request, configs, appContext, helpers) { +module.exports = function (appContext) { /* GET /finalgrades * Returns final grades with user information for all users in the given org unit based on the orgUnitId * passed in through the query parameters. */ - app.get('/finalgrades', function (req, res) { + router.get('/finalgrades', function (req, res) { const orgUnitId = req.query.orgUnitId; const classlistApiPath = '/d2l/api/le/1.12/' + orgUnitId + '/classlist/'; const accessToken = req.cookies[configs.cookieName].accessToken; @@ -117,4 +122,6 @@ module.exports = function (app, request, configs, appContext, helpers) { } ); } + + return router; }; diff --git a/src/apis/profileimage.js b/src/apis/profileimage.js index 51a2a6e..b6d6bbd 100644 --- a/src/apis/profileimage.js +++ b/src/apis/profileimage.js @@ -1,11 +1,17 @@ 'use strict'; -module.exports = function (app, request, configs, appContext, rootDirectory, helpers) { +const configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); + +module.exports = function (appContext, rootDirectory) { /* GET /uploadprofileimage * Updates the profile image for the user that matches the UserId passed into the route. */ - app.get('/uploadprofileimage', function (req, res) { + router.get('/uploadprofileimage', function (req, res) { const userId = req.query.userId; const apiPath = '/d2l/api/lp/1.9/profile/user/' + userId + '/image'; const accessToken = req.cookies[configs.cookieName].accessToken; @@ -49,5 +55,6 @@ module.exports = function (app, request, configs, appContext, rootDirectory, hel } }); - + + return router; }; diff --git a/src/apis/whoami.js b/src/apis/whoami.js index f9ff247..fd22271 100644 --- a/src/apis/whoami.js +++ b/src/apis/whoami.js @@ -1,11 +1,17 @@ 'use strict'; -module.exports = function (app, request, configs, appContext, helpers) { +const configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); + +module.exports = function (appContext) { /* GET /whoami * Returns the who am I information based on the currently authenticated user. */ - app.get('/whoami', function (req, res) { + router.get('/whoami', function (req, res) { const apiPath = '/d2l/api/lp/1.9/users/whoami'; const accessToken = req.cookies[configs.cookieName].accessToken; if (accessToken) { @@ -46,4 +52,5 @@ module.exports = function (app, request, configs, appContext, helpers) { }); + return router; }; diff --git a/src/authorization/idkeyauth.js b/src/authorization/idkeyauth.js index 3705f5d..4c235ec 100644 --- a/src/authorization/idkeyauth.js +++ b/src/authorization/idkeyauth.js @@ -1,11 +1,16 @@ 'use strict'; -module.exports = function (app, configs, appContext, helpers) { +const configs = require('../configurations'), + helpers = require('../helpers'), + express = require('express'), + router = express.Router(); + +module.exports = function (appContext) { /* GET /idkeyauth * This route is used to initiate the ID/Key Authentication workflow. */ - app.get('/idkeyauth', function(req, res) { + router.get('/idkeyauth', function(req, res) { const callbackTarget = helpers.getIdKeyRedirectUri(req); const getTokensUrl = appContext.createUrlForAuthentication(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, callbackTarget); res.redirect(getTokensUrl); @@ -16,11 +21,13 @@ module.exports = function (app, configs, appContext, helpers) { * workflow they will be redirected back to this route. The returned UserKey and UserId is then saved * in a cookie so that later requests can be signed using the user's context. */ - app.get('/idkeycallback', function(req, res) { + router.get('/idkeycallback', function(req, res) { const callbackRoute = req.url; const userContext = appContext.createUserContext(configs.instanceScheme + '//' + configs.instanceUrl, configs.instancePort, callbackRoute); res.cookie(configs.cookieName, { userKey: userContext.userKey, userId: userContext.userId }, configs.cookieOptions); res.redirect('/?authenticationType=idkeyauth'); }); + return router; + }; diff --git a/src/authorization/oauth.js b/src/authorization/oauth.js index c357c67..105187d 100644 --- a/src/authorization/oauth.js +++ b/src/authorization/oauth.js @@ -1,8 +1,13 @@ 'use strict'; -const querystring = require('querystring'); +const querystring = require('querystring'), + configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); -module.exports = function (app, request, configs, helpers) { +module.exports = function () { /* GET /oauth * This endpoint is used to redirect the user to the authentication route @@ -10,7 +15,7 @@ module.exports = function (app, request, configs, helpers) { * that they want allow this application to make API requests on * their behalf. */ - app.get('/oauth', function(req, res) { + router.get('/oauth', function(req, res) { // The state value is hardcoded for the sample but normally should change with each request to the // authentication endpoint and then stored securely. Please read the configuration.md readme for @@ -32,7 +37,7 @@ module.exports = function (app, request, configs, helpers) { * method takes the authorization code and exchanges it for * the token(stores it in a cookie) that can then be used to make API requests. */ - app.get('/oauthcallback', function(req, res) { + router.get('/oauthcallback', function(req, res) { const authorizationCode = req.query.code; const state = req.query.state; if (state !== configs.state) { @@ -63,4 +68,6 @@ module.exports = function (app, request, configs, helpers) { } }); }); + + return router; }; diff --git a/src/remote-plugins/courseimport-cim.js b/src/remote-plugins/courseimport-cim.js index 74dfb8f..022c7a2 100644 --- a/src/remote-plugins/courseimport-cim.js +++ b/src/remote-plugins/courseimport-cim.js @@ -1,18 +1,26 @@ 'use strict'; -module.exports = function (app, request, configs, appContext, path, directory, helpers) { +const path = require('path'), + configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); + + +module.exports = function (appContext, directory) { /* GET /courseimportselection * Returns the courseimport-cim html page for presentation to the user within Brightspace. */ - app.get('/courseimportselection', function(req, res) { + router.get('/courseimportselection', function(req, res) { res.sendFile(path.join(directory+'/html/courseimport-cim.html')); }); /* POST /lti/isfcontent * The LTI endpoint for a Course Import (CIM) remote plugin. */ - app.post('/lti/courseimport', function (req, res) { + router.post('/lti/courseimport', function (req, res) { const url = req.protocol + '://' + req.get('host') + '/lti/courseimport'; if (!helpers.verifyLtiRequest(url, req.body, configs.ltiSecret)) { console.log('Could not verify the LTI Request. OAuth 1.0 Validation Failed'); @@ -34,7 +42,7 @@ module.exports = function (app, request, configs, appContext, path, directory, h * Returns the details for the request that needs to be submitted through the form back * to Brightspace in order to import the selected package into Brightspace. */ - app.get('/getcourseimportdetails', function (req, res) { + router.get('/getcourseimportdetails', function (req, res) { // Generate the url to the package based on the user's selection, sent through the query param named // package. const fileUrl = 'https://github.com/Brightspace/Extensibility-Samples/raw/master/content/importpackage/' + req.query.package; @@ -70,4 +78,5 @@ module.exports = function (app, request, configs, appContext, path, directory, h res.send(JSON.stringify(responseObject)); }); + return router; }; diff --git a/src/remote-plugins/isf-cim.js b/src/remote-plugins/isf-cim.js index c5582cd..7653fad 100644 --- a/src/remote-plugins/isf-cim.js +++ b/src/remote-plugins/isf-cim.js @@ -1,18 +1,25 @@ 'use strict'; -module.exports = function (app, request, configs, appContext, path, directory, helpers) { +const path = require('path'), + configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); + +module.exports = function (appContext, directory) { /* GET /isfselection * Returns the isf-cim html page for presentation to the user within Brightspace. */ - app.get('/isfselection', function(req, res) { + router.get('/isfselection', function(req, res) { res.sendFile(path.join(directory+'/html/isf-cim.html')); }); /* POST /lti/isfcontent * The LTI endpoint for a Insert Stuff (CIM) remote plugin. */ - app.post('/lti/isfcontent', function (req, res) { + router.post('/lti/isfcontent', function (req, res) { const url = req.protocol + '://' + req.get('host') + '/lti/isfcontent'; if (!helpers.verifyLtiRequest(url, req.body, configs.ltiSecret)) { console.log('Could not verify the LTI Request. OAuth 1.0 Validation Failed'); @@ -34,7 +41,7 @@ module.exports = function (app, request, configs, appContext, path, directory, h * Returns the details for the request that needs to be submitted through the form back * to Brightspace in order to insert the stuff into Brightspace. */ - app.get('/getisfdetails', function (req, res) { + router.get('/getisfdetails', function (req, res) { const imageUrl = req.protocol + '://' + req.get('host') + '/content/isf/' + req.query.image; const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; @@ -75,4 +82,5 @@ module.exports = function (app, request, configs, appContext, path, directory, h }); + return router; }; diff --git a/src/remote-plugins/quicklink-cim.js b/src/remote-plugins/quicklink-cim.js index 711fee6..7803cdd 100644 --- a/src/remote-plugins/quicklink-cim.js +++ b/src/remote-plugins/quicklink-cim.js @@ -1,18 +1,25 @@ 'use strict'; -module.exports = function (app, request, configs, appContext, path, directory, helpers) { +const path = require('path'), + configs = require('../configurations'), + helpers = require('../helpers'), + request = require('superagent'), + express = require('express'), + router = express.Router(); + +module.exports = function (appContext, directory) { /* GET /quicklinkselection * Returns the quicklink-cim html page for presentation to the user within Brightspace. */ - app.get('/quicklinkselection', function(req, res) { + router.get('/quicklinkselection', function(req, res) { res.sendFile(path.join(directory+'/html/quicklink-cim.html')); }); /* POST /lti/quicklinkcontent * The LTI endpoint for a Quicklink (CIM) remote plugin. */ - app.post('/lti/quicklinkcontent', function (req, res) { + router.post('/lti/quicklinkcontent', function (req, res) { const url = req.protocol + '://' + req.get('host') + '/lti/quicklinkcontent'; if (!helpers.verifyLtiRequest(url, req.body, configs.ltiSecret)) { console.log('Could not verify the LTI Request. OAuth 1.0 Validation Failed'); @@ -34,7 +41,7 @@ module.exports = function (app, request, configs, appContext, path, directory, h * Returns the details for the request that needs to be submitted through the form back * to Brightspace in order to add the content. */ - app.get('/getquicklinkdetails', function (req, res) { + router.get('/getquicklinkdetails', function (req, res) { const fileUrl = req.protocol + '://' + req.get('host') + '/content/quicklink/' + req.query.link; const contentItemReturnUrl = req.cookies['lti-request'].contentItemReturnUrl; @@ -73,4 +80,5 @@ module.exports = function (app, request, configs, appContext, path, directory, h res.send(JSON.stringify(responseObject)); }); + return router; }; diff --git a/src/remote-plugins/statics.js b/src/remote-plugins/statics.js index 11a4059..3c41d0a 100644 --- a/src/remote-plugins/statics.js +++ b/src/remote-plugins/statics.js @@ -1,6 +1,8 @@ 'use strict'; -module.exports = function (app, express, rootDirectory) { +const express = require('express'); + +module.exports = function (app, rootDirectory) { // Setup static folders for serving up content to the Remote Plugins. app.use('/content/isf', express.static(rootDirectory + '/content/isf')); app.use('/content/quicklink', express.static(rootDirectory + '/content/quicklink')); From 45a2e955516baa22917d2007480a95d21afa58b6 Mon Sep 17 00:00:00 2001 From: David Wagler Date: Fri, 22 Sep 2017 11:31:57 -0400 Subject: [PATCH 4/4] Update let to const for the objects passed back in the CIM plugins --- src/remote-plugins/courseimport-cim.js | 2 +- src/remote-plugins/isf-cim.js | 2 +- src/remote-plugins/quicklink-cim.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/remote-plugins/courseimport-cim.js b/src/remote-plugins/courseimport-cim.js index 022c7a2..88eb56e 100644 --- a/src/remote-plugins/courseimport-cim.js +++ b/src/remote-plugins/courseimport-cim.js @@ -61,7 +61,7 @@ module.exports = function (appContext, directory) { ] }; - let responseObject = { + const responseObject = { lti_message_type: 'ContentItemSelection', lti_version: 'LTI-1p0', content_items: JSON.stringify(contentItems), diff --git a/src/remote-plugins/isf-cim.js b/src/remote-plugins/isf-cim.js index 7653fad..321b997 100644 --- a/src/remote-plugins/isf-cim.js +++ b/src/remote-plugins/isf-cim.js @@ -64,7 +64,7 @@ module.exports = function (appContext, directory) { ] }; - let responseObject = { + const responseObject = { lti_message_type: 'ContentItemSelection', lti_version: 'LTI-1p0', content_items: JSON.stringify(contentItems), diff --git a/src/remote-plugins/quicklink-cim.js b/src/remote-plugins/quicklink-cim.js index 7803cdd..c481772 100644 --- a/src/remote-plugins/quicklink-cim.js +++ b/src/remote-plugins/quicklink-cim.js @@ -63,7 +63,7 @@ module.exports = function (appContext, directory) { ] }; - let responseObject = { + const responseObject = { lti_message_type: 'ContentItemSelection', lti_version: 'LTI-1p0', content_items: JSON.stringify(contentItems),