caccl
Version:
Canvas App Complete Connection Library: an all-in-one library for connecting your app to Canvas, handling lti, access tokens, and api.
730 lines • 39.4 kB
JavaScript
;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getLaunchInfo = exports.getSelfLaunchState = exports.redirectToSelfLaunch = exports.redirectToAuth = exports.getAPI = exports.handlePassback = exports.getStatus = exports.sendRequest = void 0;
// Import libs
var express_1 = __importDefault(require("express"));
// Import caccl libs
var caccl_send_request_1 = __importDefault(require("caccl-send-request"));
var caccl_lti_1 = __importStar(require("caccl-lti"));
Object.defineProperty(exports, "getLaunchInfo", { enumerable: true, get: function () { return caccl_lti_1.getLaunchInfo; } });
var caccl_api_forwarder_1 = __importDefault(require("caccl-api-forwarder"));
var caccl_authorizer_1 = __importStar(require("caccl-authorizer"));
var caccl_api_1 = __importDefault(require("caccl-api"));
var caccl_grade_passback_1 = __importDefault(require("caccl-grade-passback"));
var caccl_error_1 = __importDefault(require("caccl-error"));
var LaunchType_1 = __importDefault(require("caccl-lti/lib/shared/types/LaunchType"));
var ErrorCode_1 = __importDefault(require("./shared/types/ErrorCode"));
// Import shared constants
var CACCL_PATHS_1 = __importDefault(require("./shared/constants/CACCL_PATHS"));
var CACCL_SIM_TOOL_ID_1 = __importDefault(require("./shared/constants/CACCL_SIM_TOOL_ID"));
// Import helpers
var genExpressApp_1 = __importDefault(require("./helpers/genExpressApp"));
// Check if this is a dev environment
var thisIsDevEnvironment = (process.env.NODE_ENV === 'development');
// Force ignoring SSL issues
if (thisIsDevEnvironment) {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
}
/*------------------------------------------------------------------------*/
/* Caching */
/*------------------------------------------------------------------------*/
// Store credentials from most recent initialization
var mostRecentInstallationCreds;
// Store whether certain features are enabled
var authEnabled;
/*------------------------------------------------------------------------*/
/* Functions */
/*------------------------------------------------------------------------*/
/*----------------------------------------*/
/* Request Sender */
/*----------------------------------------*/
/**
* Send a request to another server
* @author Gabe Abrams
* @param opts object containing all arguments
* @param opts.host hostname of the destination server
* @param opts.path path of the server endpoint
* @param opts.method http method of the request
* @param [opts.params] object containing body/query parameters. Only allows
* one level of object nesting (values that are objects must be stringified
* using JSON.stringify and then parsed on the server)
* @param [opts.headers] object containing additional headers to include
* @param [opts.numRetries=0] number of times to retry the request if a network
* error occurs
* @param [opts.responseType=JSON] expected response type
* @returns response object
*/
var sendRequest = function (opts) { return __awaiter(void 0, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, (0, caccl_send_request_1.default)(opts)];
});
}); };
exports.sendRequest = sendRequest;
/*----------------------------------------*/
/* Self Launch State */
/*----------------------------------------*/
/**
* Get current self launch state
* @author Gabe Abrams
* @param req express request instance
* @param [dontClear] if true, self launch state will be left until the next
* time someone calls getSelfLaunchState
* @returns self launch state or undefined if a self launch did not recently
* occur
*/
var getSelfLaunchState = function (req, dontClear) { return __awaiter(void 0, void 0, void 0, function () {
var selfLaunchState;
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
selfLaunchState = (_a = req.session.selfLaunchState) !== null && _a !== void 0 ? _a : undefined;
if (!!dontClear) return [3 /*break*/, 2];
// Delete
delete req.session.selfLaunchState;
// Save session
return [4 /*yield*/, new Promise(function (resolve) {
req.session.save(resolve);
})];
case 1:
// Save session
_b.sent();
_b.label = 2;
case 2:
// Return self launch state
return [2 /*return*/, selfLaunchState];
}
});
}); };
exports.getSelfLaunchState = getSelfLaunchState;
/*----------------------------------------*/
/* Status Checker */
/*----------------------------------------*/
/**
* Get CACCL status from the server
* @author Gabe Abrams
* @param req express request instance
* @returns status
*/
var getStatus = function (req) { return __awaiter(void 0, void 0, void 0, function () {
var _a, launched, launchInfo, authorized, err_1, status;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = (0, caccl_lti_1.getLaunchInfo)(req), launched = _a.launched, launchInfo = _a.launchInfo;
authorized = false;
if (!authEnabled) return [3 /*break*/, 4];
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, (0, caccl_authorizer_1.getAccessToken)(req)];
case 2:
authorized = !!(_b.sent());
return [3 /*break*/, 4];
case 3:
err_1 = _b.sent();
// Error occurred while getting the access token. Not authorized
authorized = false;
return [3 /*break*/, 4];
case 4:
if (launched) {
status = {
launched: launched,
launchInfo: launchInfo,
authorized: authorized,
};
}
else {
status = {
launched: false,
};
}
// Return status object
return [2 /*return*/, status];
}
});
}); };
exports.getStatus = getStatus;
/*----------------------------------------*/
/* Grade Passback */
/*----------------------------------------*/
/**
* Send grade passback to Canvas
* @author Gabe Abrams
* @param opts object containing all arguments
* @param opts.req express request instance
* @param [opts.text] the text of the submission. If this is
* included, url cannot be included
* @param [opts.url] a url to send as the student's
* submission. If this is included, text cannot be included
* @param [opts.score] the student's score on this assignment
* @param [opts.percent] the student's score as a percent
* (0-100) on the assignment
* @param [opts.submittedAt=now] a timestamp for when the
* student submitted the grade. The type must either be a Date object or
* an ISO 8601 formatted string
*/
var handlePassback = function (opts) { return __awaiter(void 0, void 0, void 0, function () {
var req, text, url, score, percent, submittedAt, _a, launched, launchInfo, outcome, consumerSecret, success, err_2;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
req = opts.req, text = opts.text, url = opts.url, score = opts.score, percent = opts.percent, submittedAt = opts.submittedAt;
_a = (0, caccl_lti_1.getLaunchInfo)(req), launched = _a.launched, launchInfo = _a.launchInfo;
// Make sure the user has a valid session
if (!launched) {
throw new caccl_error_1.default({
message: 'You cannot finish this assignment because your session has expired.',
code: ErrorCode_1.default.NoLaunchInfo,
});
}
// Make sure the request contains something to submit
if (!text && !url) {
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because the request was empty.',
code: ErrorCode_1.default.NoPassbackContent,
});
}
// Make sure we have passback info
if (launchInfo.launchType !== LaunchType_1.default.Assignment) {
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because we don\'t have a valid Canvas LTI assignment launch.',
code: ErrorCode_1.default.NoAssignmentLaunch,
});
}
outcome = launchInfo.outcome;
if (!outcome.sourcedId
|| !outcome.url) {
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because we don\'t have the information from Canvas to send the request.',
code: ErrorCode_1.default.NoOutcomeInfo,
});
}
// Make sure Canvas can accept the request
if ((url && !outcome.urlSubmissionAccepted)
|| (text && !outcome.textSubmissionAccepted)
|| (score && !outcome.totalScoreAccepted)
|| (submittedAt && !outcome.submittedAtAccepted)) {
// Canvas cannot accept our request
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because Canvas does not support all of the parameters we want to send.',
code: ErrorCode_1.default.PassbackParamNotAccepted,
});
}
// Make sure we have credentials
if (!mostRecentInstallationCreds) {
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because CACCL is not yet initialized.',
code: ErrorCode_1.default.PassbackBeforeCACCLInitialized,
});
}
consumerSecret = mostRecentInstallationCreds[launchInfo.consumerKey];
if (!consumerSecret) {
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because this app is not set up to be installed for this Canvas host.',
code: ErrorCode_1.default.PassbackParamNotAccepted,
});
}
_b.label = 1;
case 1:
_b.trys.push([1, 3, , 4]);
return [4 /*yield*/, (0, caccl_grade_passback_1.default)({
request: {
text: text,
url: url,
score: score,
percent: percent,
submittedAt: (submittedAt || (new Date()).toISOString()),
},
info: {
sourcedId: outcome.sourcedId,
url: outcome.url,
},
credentials: {
consumerKey: launchInfo.consumerKey,
consumerSecret: consumerSecret,
},
})];
case 2:
success = _b.sent();
// Force failure if handlePassback fails
if (!success) {
throw new Error();
}
return [3 /*break*/, 4];
case 3:
err_2 = _b.sent();
throw new caccl_error_1.default({
message: 'We could not send grades back to Canvas via passback because Canvas did not accept the appropriate updates.',
code: ErrorCode_1.default.PassbackUnsuccessful,
});
case 4: return [2 /*return*/];
}
});
}); };
exports.handlePassback = handlePassback;
/*----------------------------------------*/
/* API */
/*----------------------------------------*/
/**
* Get a copy of the CACCL API instance for the current user (the current user
* must be launched and authorized)
* @param opts object containing all arguments
* @param opts.req express request instance
* @param [opts.numRetries=3] default number of retries per request
* @param [opts.itemsPerPage=100] default number of items to request
* per page
* @returns CACCL API instance
*/
var getAPI = function (opts) { return __awaiter(void 0, void 0, void 0, function () {
var _a, launched, launchInfo, accessToken;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
// Error if auth is disabled
if (!authEnabled) {
throw new caccl_error_1.default({
message: 'Auth is not enabled, so you cannot get a copy of the API.',
code: ErrorCode_1.default.NoAPIAuthDisabled,
});
}
_a = (0, caccl_lti_1.getLaunchInfo)(opts.req), launched = _a.launched, launchInfo = _a.launchInfo;
if (!launched || !launchInfo.canvasHost) {
throw new caccl_error_1.default({
message: 'We could not get a copy of the CACCL API because the current user has not launched via LTI.',
code: ErrorCode_1.default.CantInitAPIWithoutLaunch,
});
}
return [4 /*yield*/, (0, caccl_authorizer_1.getAccessToken)(opts.req)];
case 1:
accessToken = _b.sent();
if (!accessToken) {
throw new caccl_error_1.default({
message: 'We could not get a copy of the CACCL API because the current user is not authorized with Canvas.',
code: ErrorCode_1.default.CantInitAPIWithoutAuth,
});
}
// Initialize the API instance
return [2 /*return*/, (0, caccl_api_1.default)({
numRetries: opts.numRetries,
itemsPerPage: opts.itemsPerPage,
canvasHost: launchInfo.canvasHost,
defaultCourseId: launchInfo.courseId,
accessToken: accessToken,
})];
}
});
}); };
exports.getAPI = getAPI;
/*----------------------------------------*/
/* Redirects */
/*----------------------------------------*/
/**
* Redirect the user to the API authorization screen. Useful if the user
* is not authorized and you want to be authorized. This is usually
* not necessary if lti.authorizeAfterLaunch is set to true when
* initializing CACCL on the server. Only functional if Canvas API auth is
* enabled via CACCL on the server.
* @author Gabe Abrams
* @param res express response object
*/
var redirectToAuth = function (res) {
return res.redirect(CACCL_PATHS_1.default.AUTHORIZE);
};
exports.redirectToAuth = redirectToAuth;
/**
* Redirect the user to the self-launch process. Only functional if
* self-launch is enabled via CACCL on the server.
* @author Gabe Abrams
* @param opts object containing all arguments
* @param opts.req express request object
* @param opts.res express response object
* @param opts.courseId the Canvas id of the course to launch from
* @param [opts.canvasHost=defaultCanvasHost] host of the
* Canvas instance containing the course to launch from
* @param [opts.appId=look up appId] id for this app as it is installed in
* Canvas in the course
* @param [selfLaunchState='self launch occurred with no state passed in'] self
* launch state to pass through (retrievable via getSelfLaunchState function).
* This is useful if you need to keep track of state through the self launch
* process. Must be JSONifiable.
*/
var redirectToSelfLaunch = function (opts) {
var _a;
// Store self launch state
opts.req.session.selfLaunchState = ((_a = opts.selfLaunchState) !== null && _a !== void 0 ? _a : 'self launch occurred with no state passed in');
// Save state asynchronously
opts.req.session.save();
// Redirect user
return opts.res.redirect((0, caccl_lti_1.getSelfLaunchURL)(__assign(__assign({}, opts), { appId: (thisIsDevEnvironment
? CACCL_SIM_TOOL_ID_1.default
: opts.appId) })));
};
exports.redirectToSelfLaunch = redirectToSelfLaunch;
/*------------------------------------------------------------------------*/
/* Initializer */
/*------------------------------------------------------------------------*/
/**
* Initialize a CACCL app server
* @author Gabe Abrams
* @param [opts] object containing all arguments
* @param [opts.lti] object containing all LTI configuration params
* @param [opts.lti.installationCredentials=env vars] an object where keys are
* LTI consumer keys and values are LTI shared secrets. If excluded, defaults
* to { [env.CONSUMER_KEY | 'consumer_key']: (env.CONSUMER_SECRET | 'consumer_secret') }
* @param [opts.lti.dontAuthorizeAfterLaunch] if false, redirect the user to
* the CACCL authorizer after a successful LTI launch. Note: if api/auth is
* disabled, dontAuthorizeAfterLaunch will be set to true automatically
* @param [opts.lti.initNonceStore=memory store factory] a function that creates
* a store for keeping track of used nonces
* @param [opts.lti.selfLaunch] if included, self launches will be enabled and
* the app will be able to launch itself (redirect to the Canvas tool inside
* the course of interest)
* @param [opts.lti.selfLaunch.initAppIdStore=memory store factory] a function
* that creates a store for keeping track of appIds
* @param [opts.lti.selfLaunch.hostAppIdMap] map of appIds where
* keys are canvasHost strings and values are the appIds. Include appIds
* here if the appId is the same across the whole Canvas instance
* @param [opts.lti.selfLaunch.courseAppIdMap] two-level map of appIds where the
* first key is the canvas host, the second key is the courseId, and values
* are the appIds. Include appIds here if the app is unique to specific
* courses
* @param [opts.lti.selfLaunch.adminAccessTokenMap] map of Canvas admin access
* tokens that can be used to look up appIds when the appId is not in any of
* the appId maps. Keys are canvasHost strings and values are arrays of
* Canvas admin tokens that will be used to look up appIds. The tokens will
* be used in order: the first token will be used, then if that fails, the
* second token will be used, and so on.
* @param [opts.lti.selfLaunch.defaultCanvasHost=env.DEFAULT_CANVAS_HOST] default Canvas host to use in
* self launches
* @param [opts.api] object containing all api and authorization configuration
* params. Must be included if integrating with the Canvas API
* @param [opts.api.developerCredentials] map of developer credentials
* to use when authorizing this app with canvas. If excluded, defaults to
* { [env.DEFAULT_CANVAS_HOST]: { [env..CLIENT_ID]: env..CLIENT_SECRET } }
* @param [opts.api.initTokenStore=memory store factory] a function that
* creates a store for keeping track of user's API tokens and auth status
* @param [opts.api.disableClientSideAPI] if true, do not allow the client
* to send Canvas API requests on behalf of the current user's auth
* credentials
* @param [opts.api.scopes] list of scope strings
* (e.g. url:GET|/api/v1/courses). These scopes will be included
* in all authorization requests
* @param [opts.express] object containing all express configuration params.
* If excluded, express is initialized with all defaults
* @param [opts.express.app] manually-initialized express app that uses
* express-session. If excluded,
* express is initialized using all other properties of opts.express. If
* included, all other properties of opts.express are ignored
* @param [opts.express.port=env.PORT || 8080] port to listen to
* @param [opts.express.sessionSecret=env.SESSION_SECRET || randomly generated]
* session secret to use when encrypting sessions
* @param [opts.express.cookieName=env.COOKIE_NAME || randomly generated] cookie
* name to use when identifying this app's session. Must not contain tabs or
* spaces
* @param [opts.express.sessionMins=env.SESSION_MINS || 360] number of minutes
* the session should last for
* @param [opts.express.sessionStore=memory store] express-session store
* @param [opts.express.preprocessor] function to call after express app
* created but before any CACCL routes are added
* @param [opts.express.postprocessor] function to call after CACCL routes are
* added but before the ('*' => react app) route is added. This is great for
* adding other server-side routes
*/
var initCACCL = function (opts) {
if (opts === void 0) { opts = {}; }
return __awaiter(void 0, void 0, void 0, function () {
var app, expressAppPreprocessor, installationCredentials, developerCredentials, disableClientSideAPI, initialWorkingDirectory, buildDir_1;
var _a, _b;
var _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
return __generator(this, function (_r) {
switch (_r.label) {
case 0:
app = (_c = opts.express) === null || _c === void 0 ? void 0 : _c.app;
if (!app) {
app = (0, genExpressApp_1.default)(opts);
}
// Add cross-origin handler for development mode
if (thisIsDevEnvironment) {
app.use(function (req, res, next) {
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000');
res.setHeader('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
res.setHeader('Access-Control-Request-Headers', '*');
if (req.method === 'OPTIONS') {
return res.sendStatus(200);
}
next();
});
}
expressAppPreprocessor = (_d = opts.express) === null || _d === void 0 ? void 0 : _d.preprocessor;
if (expressAppPreprocessor) {
expressAppPreprocessor(app);
}
// Check if auth is enabled
authEnabled = !!(
// Options are passed in
(opts.api && opts.api.developerCredentials)
// Options are in environment
|| (process.env.DEFAULT_CANVAS_HOST
&& process.env.CLIENT_ID
&& process.env.CLIENT_SECRET)
// Using development environment fake credentials
|| thisIsDevEnvironment);
installationCredentials = (thisIsDevEnvironment
? { consumer_key: 'consumer_secret' } // Dummy values for Canvas sim
: (
// Passed in map
(_f = (_e = opts.lti) === null || _e === void 0 ? void 0 : _e.installationCredentials) !== null && _f !== void 0 ? _f : (_a = {},
_a[(_g = process.env.CONSUMER_KEY) !== null && _g !== void 0 ? _g : 'consumer_key'] = ((_h = process.env.CONSUMER_SECRET) !== null && _h !== void 0 ? _h : 'consumer_secret'),
_a)));
// Initialize LTI
return [4 /*yield*/, (0, caccl_lti_1.default)(__assign(__assign({}, ((_j = opts === null || opts === void 0 ? void 0 : opts.lti) !== null && _j !== void 0 ? _j : {})), { dontAuthorizeAfterLaunch: !!(
// Flag is true
((_k = opts === null || opts === void 0 ? void 0 : opts.lti) === null || _k === void 0 ? void 0 : _k.dontAuthorizeAfterLaunch)
// Auth is not enabled
|| !authEnabled), app: app, installationCredentials: installationCredentials }))];
case 1:
// Initialize LTI
_r.sent();
// Store installation credentials for later
mostRecentInstallationCreds = installationCredentials;
if (!authEnabled) return [3 /*break*/, 3];
developerCredentials = (thisIsDevEnvironment
? {
'localhost:8088': {
clientId: 'client_id',
clientSecret: 'client_secret',
},
} // Dummy values for Canvas sim
: (
// Passed in map
(_m = (_l = opts.api) === null || _l === void 0 ? void 0 : _l.developerCredentials) !== null && _m !== void 0 ? _m : (_b = {},
_b[(_o = process.env.DEFAULT_CANVAS_HOST) !== null && _o !== void 0 ? _o : 'localhost:8088'] = {
clientId: String(process.env.CLIENT_ID),
clientSecret: String(process.env.CLIENT_SECRET),
},
_b)));
// Initialize auth
return [4 /*yield*/, (0, caccl_authorizer_1.default)(__assign(__assign({}, opts === null || opts === void 0 ? void 0 : opts.api), { app: app, developerCredentials: developerCredentials }))];
case 2:
// Initialize auth
_r.sent();
disableClientSideAPI = !!((_p = opts === null || opts === void 0 ? void 0 : opts.api) === null || _p === void 0 ? void 0 : _p.disableClientSideAPI);
// Initialize auth forwarder
if (!disableClientSideAPI) {
// Client-side API is enabled. Add forwarder
(0, caccl_api_forwarder_1.default)({ app: app });
}
_r.label = 3;
case 3:
/*----------------------------------------*/
/* Server-side Endpoints */
/*----------------------------------------*/
/* ------------- Status ------------- */
/**
* Get the CACCL status of the current user
* @author Gabe Abrams
* @returns success response
*/
app.get(CACCL_PATHS_1.default.STATUS, function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var status_1, err_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, getStatus(req)];
case 1:
status_1 = _a.sent();
// Send status to client
return [2 /*return*/, res.status(200).json({
success: true,
status: status_1,
})];
case 2:
err_3 = _a.sent();
return [2 /*return*/, res.status(500).json({
success: false,
message: (err_3.message || 'We could not get the current user\'s status.'),
code: (err_3.code || ErrorCode_1.default.StatusFailed),
})];
case 3: return [2 /*return*/];
}
});
}); });
/* --------- Grade Passback --------- */
/**
* Handle a client's request to perform grade passback
* @author Gabe Abrams
* @param {string} [text] the text of the submission. If this is
* included, url cannot be included
* @param {string} [url] a url to send as the student's submission.
* If this is included, text cannot be included
* @param {number} [score] the student's score on this assignment
* @param {number} [percent] the student's score as a percent (0-100)
* on the assignment
* @param {string} [submittedAt=now] a timestamp for when the
* student submitted the grade. The type must either be an
* ISO 8601 formatted string
*/
app.post(CACCL_PATHS_1.default.HANDLE_PASSBACK, function (req, res) { return __awaiter(void 0, void 0, void 0, function () {
var text, url, score, percent, submittedAt, err_4;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
text = (req.body.text
? String(req.body.text)
: undefined);
url = (req.body.url
? String(req.body.url)
: undefined);
score = (req.body.score
? Number.parseFloat(req.body.score)
: undefined);
percent = (req.body.percent
? Number.parseFloat(req.body.percent)
: undefined);
submittedAt = (req.body.submittedAt
? String(req.body.submittedAt)
: undefined);
// Call helper
return [4 /*yield*/, handlePassback({
req: req,
text: text,
url: url,
score: score,
percent: percent,
submittedAt: submittedAt,
})];
case 1:
// Call helper
_a.sent();
// Send a success response
return [2 /*return*/, res.status(200).json({
success: true,
})];
case 2:
err_4 = _a.sent();
return [2 /*return*/, res.status(500).json({
success: false,
message: (err_4.message || 'An unknown error occurred while attempting to send a grade passback to Canvas.'),
code: (err_4.code || ErrorCode_1.default.PassbackUnsuccessful),
})];
case 3: return [2 /*return*/];
}
});
}); });
/*----------------------------------------*/
/* React Client */
/*----------------------------------------*/
// Run postprocessor first
if ((_q = opts === null || opts === void 0 ? void 0 : opts.express) === null || _q === void 0 ? void 0 : _q.postprocessor) {
opts.express.postprocessor(app);
}
initialWorkingDirectory = (String(process.env.PWD).endsWith('/server')
? String(process.env.PWD).substring(0, String(process.env.PWD).length - '/server'.length)
: String(process.env.PWD));
// Change config for dev environment
if (thisIsDevEnvironment) {
// Print a notice
console.log('Server running in development mode. This is not safe for production use.');
// Redirect all traffic to react development port
// (delay so server can add routes)
setTimeout(function () {
app.get('*', function (req, res) {
// Redirect to the appropriate front-end site
return res.redirect("http://localhost:3000".concat(req.path));
});
}, 2000);
}
else {
buildDir_1 = "".concat(initialWorkingDirectory, "/client/build");
// Serve styles, etc
app.use(express_1.default.static(buildDir_1));
// Send frontend
app.get('*', function (req, res) {
res.sendFile("".concat(buildDir_1, "/index.html"));
});
}
return [2 /*return*/];
}
});
});
};
exports.default = initCACCL;
//# sourceMappingURL=index.js.map