scormcloud-client
Version:
A Typescript and JS client for interfacing with the ScormCloud API
1,036 lines (1,035 loc) • 50.5 kB
JavaScript
"use strict";
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ScormClient = exports.ScormClientError = exports.Util = exports.TypeChecks = void 0;
/* eslint-disable max-len */
/* eslint-disable no-prototype-builtins */
const luxon_1 = require("luxon");
const superagent_1 = __importDefault(require("superagent"));
/** @internal */
const BASE_PATH = 'https://cloud.scorm.com/api/v2';
/** @internal */
const HttpStatus = {
isSuccess: (r) => {
return !!(r.status && (r.status === 200 || r.status === 204));
},
notFound: (r) => {
return !!(r.status && r.status === 404);
},
isUnauthorized: (e) => {
if (e.status) {
return e.status === 401;
}
return !!(e.response && e.response.status === 401);
}
};
/** @internal */
exports.TypeChecks = {
containsErrorProperty: (x) => {
if (x.error) {
return true;
}
return false;
},
isErrorObject: (x) => {
if (x.message) {
return true;
}
return false;
},
isAuthToken: (x) => {
if (x.access_token) {
return true;
}
return false;
},
isHttpError: (x) => {
var _a;
if ((_a = x.response) === null || _a === void 0 ? void 0 : _a.error) {
return true;
}
return false;
}
};
/** @internal */
exports.Util = {
// eslint-disable-next-line @typescript-eslint/ban-types
hasProperty(obj, prop) {
// eslint-disable-next-line no-prototype-builtins
return obj.hasOwnProperty(prop);
},
getOption: (name, options) => {
return options === null || options === void 0 ? void 0 : options[name];
},
sleep: (milliseconds) => {
return new Promise((resolve) => setTimeout(resolve, milliseconds));
},
scormUploadType: (f) => {
if (!f) {
throw new ScormClientError('No scorm file path/name received');
}
if (f.toLowerCase().endsWith('zip')) {
return 'application/zip';
}
if (f.toLowerCase().endsWith('pdf')) {
return 'application/pdf';
}
if (f.toLowerCase().endsWith('mp3')) {
return 'audo/mpeg';
}
if (f.toLowerCase().endsWith('mp4')) {
return 'video/mp4';
}
throw new ScormClientError('Invalid scorm file type, only zip, pdf, mp3 and mp4 supported');
}
};
class ScormClientError extends Error {
/** @internal */
constructor(cause, message, httpStatus) {
const e = ScormClientError.parse(cause, message, httpStatus);
super(e.message);
this.name = 'ScormClientError';
this.message = e.message;
this.cause = e.error;
this.httpStatus = e.httpStatus;
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ScormClientError);
}
}
static parseMessage(cause, message) {
if (message) {
return message;
}
if (typeof cause === 'string') {
return cause;
}
if (exports.TypeChecks.isHttpError(cause)) {
if (cause.response.body.error) {
return cause.response.body.error;
}
return cause.response.error.message;
}
if (exports.TypeChecks.containsErrorProperty(cause)) {
return cause.error;
}
if (exports.TypeChecks.isErrorObject(cause)) {
return cause.message;
}
return 'Unknown Error';
}
static parseHttpStatus(cause, httpStatus) {
if (httpStatus) {
return httpStatus;
}
if (exports.TypeChecks.isHttpError(cause)) {
return cause.response.status;
}
return undefined;
}
static parseErrorObject(cause) {
return exports.TypeChecks.isErrorObject(cause) ? cause : undefined;
}
static parse(cause, message, httpStatus) {
return {
message: ScormClientError.parseMessage(cause, message),
httpStatus: ScormClientError.parseHttpStatus(cause, httpStatus),
error: ScormClientError.parseErrorObject(cause)
};
}
}
exports.ScormClientError = ScormClientError;
/**
*
* Usage example:
*
* ```ts
* import { ScormClient } from 'scorm-client'
*
* const client = new ScormClient(appId, secretKey, "read")
*
* // will fetch a course using a token with the default scope, in this case 'read'
* const course: Course = await client.getCourse(courseId)
*
* // will delete a course using a token with 'write' scope
* const result: SuccessIndicator = await client.deleteCourse(courseId, { scope: 'write' })
* ```
*
* You will notice that no call has been made to the `authenticate()` method before starting to use the client.
* When a method is invoked and no valid auth token for the given scope can be found, then the client will attempt
* to authenticate and fetch a token for that scope. As such, tokens are explicitly associated with a specific scope,
* and the client will internally manage a set of tokens and their associated scopes.
*
* Any future calls for a given scope will use the token associated with it. If no scope is specified (in the
* `options` for a method), then the default scope (assigned at client instantiation) will be assumed. You are able
* to manually `authenticate()` different scopes, which is simply asking the client to fetch and store a token for
* a scope, such that it is immediately available in method calls later.
*
* Note: the scope can also be a list of space separated scopes, e.g. "write:course read:registration". In such a
* case the token will be associated with all the specified scopes.
*/
class ScormClient {
/**
* @param appId A SCORM Cloud application ID
* @param secretKey The secret key for the given application ID
* @param defaultScope An auth scope or space separated list of scopes, e.g. "write:course read:registration".
* This will be the default scope used if a method on the client is invoked without specifying an explicit
* scope in the method's {@link types.Options}
* @param defaultExpiry The amount of time, in seconds, after which auth tokens should expire. If unspecified,
* it will default to 3600 (1 hour)
*/
constructor(appId, secretKey, defaultScope, defaultExpiry = 3600) {
this.authorisations = new Map();
this.appId = appId;
this.secretKey = secretKey;
this.defaultScope = defaultScope;
this.defaultExpiry = defaultExpiry;
}
getTargetScope(scope) {
if (!scope) {
return this.defaultScope;
}
if (typeof scope === 'string') {
return scope;
}
if (scope === null || scope === void 0 ? void 0 : scope.scope) {
return scope.scope;
}
return this.defaultScope;
}
getAuthToken(scope) {
const targetScope = this.getTargetScope(scope);
if (!targetScope) {
return undefined;
}
return this.authorisations.has(targetScope)
? this.authorisations.get(targetScope)
: undefined;
}
getBearerString(scope) {
const authToken = this.getAuthToken(scope);
return authToken
? `Bearer ${authToken.access_token}`
: '';
}
authorise(scope) {
return __awaiter(this, void 0, void 0, function* () {
const authToken = this.getAuthToken(scope);
if (!authToken) {
if (this.appId && this.secretKey) {
const targetScope = this.getTargetScope(scope);
if (targetScope) {
return yield this.authenticate(targetScope);
}
throw new ScormClientError('Unspecified scope', undefined, 401);
}
throw new ScormClientError('No authentication credentials found', undefined, 401);
}
if (authToken.expires_at && luxon_1.DateTime.now().valueOf() > authToken.expires_at) {
if (this.appId && this.secretKey) {
const targetScope = this.getTargetScope(scope);
if (targetScope) {
return yield this.authenticate(targetScope);
}
throw new ScormClientError('Unspecified scope', undefined, 401);
}
throw new ScormClientError('Unable to refresh authentication token', undefined, 401);
}
return authToken;
});
}
/**
* Attempt to authenticate and store an auth token, associated with a given scope
*
* @param scope An auth scope or space separated list of scopes
* @param expiry The amount of time, in seconds, after which the auth token should expire. If unspecified,
* it will use the default value provided in the constructor.
* @throws {@link ScormClientError} with an `httpStatus` of 401 on authentication failure
*/
authenticate(scope, expiry) {
return __awaiter(this, void 0, void 0, function* () {
if (!this.appId) {
throw new ScormClientError('No APP ID defined');
}
if (!this.secretKey) {
throw new ScormClientError('No SECRET KEY defined');
}
const ttl = expiry !== null && expiry !== void 0 ? expiry : this.defaultExpiry;
try {
const response = yield superagent_1.default
.post(`${BASE_PATH}/oauth/authenticate/application/token`).type('form')
.auth(this.appId, this.secretKey)
.send(`scope=${scope}`)
.send(`expiration=${ttl}`);
if (!exports.TypeChecks.isAuthToken(response.body)) {
throw new ScormClientError('Invalid auth token received');
}
const token = response.body;
token.expires_at = luxon_1.DateTime.now().plus({ seconds: ttl - 60 }).valueOf();
this.authorisations.set(scope, token);
return response.body;
}
catch (e) {
// this.authToken = undefined ??
throw new ScormClientError(e);
}
});
}
/**
* Get back a message indicating that the API is working
*
* [API Method - PingAppId](https://cloud.scorm.com/docs/v2/reference/swagger/#/ping/PingAppId)
*
* @param options Options
* @throws {@link ScormClientError} if invalid ping response received, or an error was encountered
*/
ping(options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
try {
return (yield superagent_1.default.get(`${BASE_PATH}/ping`).set('Authorization', this.getBearerString(options))).body;
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* Returns detailed information about the course.
*
* [API Method - GetCourse](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/GetCourse)
*
* @param courseId The course ID
* @param options CourseFetchOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns The requested {@link types.Course}, or `undefined` if the course was not found
*/
getCourse(courseId, options = {}) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
try {
const query = {
includeRegistrationCount: (_a = options.includeRegistrationCount) !== null && _a !== void 0 ? _a : undefined,
includeCourseMetadata: (_b = options.includeCourseMetadata) !== null && _b !== void 0 ? _b : undefined
};
return (yield superagent_1.default
.get(`${BASE_PATH}/courses/${courseId}`)
.set('Authorization', this.getBearerString(options))
.query(query)).body;
}
catch (e) {
if (HttpStatus.notFound(e)) {
return undefined;
}
throw new ScormClientError(e);
}
});
}
/**
* Returns a list of courses. Can be filtered to provide a subset of results.
*
* This request is paginated and will only provide a limited amount of resources at a time. If there are more
* results to be collected, a more token provided with the response which can be passed to get the next page
* of results. When passing this token, no other filter parameters can be sent as part of the request. The
* resources will continue to respect the filters passed in by the original request.
* [API Method - GetCourses](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/GetCourses)
*
* @param options CourseQueryOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.CourseQueryResponse} that has a property named `courses`, which is an array
* of type {@link types.Course}, or an empty array if no courses were found. It also includes a
* '`more`' property which, if present, can be used to retrieve the next paginated set of results
*/
getCourses(options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
try {
const query = {
since: (_a = options.since) !== null && _a !== void 0 ? _a : undefined,
until: (_b = options.until) !== null && _b !== void 0 ? _b : undefined,
datetimeFilter: (_c = options.datetimeFilter) !== null && _c !== void 0 ? _c : undefined,
tags: (_d = options.tags) !== null && _d !== void 0 ? _d : undefined,
filter: (_e = options.filter) !== null && _e !== void 0 ? _e : undefined,
filterBy: (_f = options.filterBy) !== null && _f !== void 0 ? _f : undefined,
orderBy: (_g = options.orderBy) !== null && _g !== void 0 ? _g : undefined,
more: (_h = options.more) !== null && _h !== void 0 ? _h : undefined,
includeCourseMetadata: (_j = options.includeCourseMetadata) !== null && _j !== void 0 ? _j : undefined,
includeRegistrationCount: (_k = options.includeRegistrationCount) !== null && _k !== void 0 ? _k : undefined
};
return (yield superagent_1.default
.get(`${BASE_PATH}/courses`)
.set('Authorization', this.getBearerString(options))
.query(query)).body;
}
catch (e) {
if (HttpStatus.notFound(e)) {
throw new ScormClientError(e, 'Bad request, likely due to invalid query options', 400);
}
throw new ScormClientError(e);
}
});
}
/**
* Creates a course from a package uploaded from your file system. The package will be sent as part of the request
* and will be stored in SCORM Cloud. An import job ID will be returned, which can be used with {@link getCourseImportStatus}
* to view the status of the import. Courses represent the learning material a learner will progress through.
* Note: The import job ID is only valid for one week after the course import finishes.
*
* [API Method - CreateUploadAndImportCourseJob](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/CreateUploadAndImportCourseJob)
*
* @param courseId The course ID
* @param filePath The path to the file that must be imported to SCORM Cloud
* @param options CourseImportOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.CourseImportResponse} if successfull
*/
importCourse(courseId, filePath, options = {}) {
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
if (!filePath) {
throw new ScormClientError('No filePath provided');
}
const fileType = exports.Util.scormUploadType(filePath);
const authString = this.getBearerString(options);
try {
const query = {
courseId,
mayCreateNewVersion: (_a = options.mayCreateNewVersion) !== null && _a !== void 0 ? _a : undefined,
postbackUrl: (_b = options.postbackUrl) !== null && _b !== void 0 ? _b : undefined
};
function postUploadJob(filePath, fileType, authString, options) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
const contentMetadata = (_a = options.contentMetadata) !== null && _a !== void 0 ? _a : undefined;
if (contentMetadata) {
return yield superagent_1.default
.post(`${BASE_PATH}/courses/importJobs/upload`).type('form')
.set('Authorization', authString)
.set('uploadedContentType', fileType)
.query(query)
.send({ contentMetadata })
.attach('file', filePath);
}
return yield superagent_1.default
.post(`${BASE_PATH}/courses/importJobs/upload`).type('form')
.set('Authorization', authString)
.set('uploadedContentType', fileType)
.query(query)
.attach('file', filePath);
});
}
const response = yield postUploadJob(filePath, fileType, authString, options);
if (!response.body.result) {
return {
courseId: undefined,
importJobId: undefined
};
}
if (!options.waitForResult) {
return {
courseId,
importJobId: response.body.result,
importJobResult: undefined
};
}
yield exports.Util.sleep(options.waitForResult);
const importJobResult = yield this.getCourseImportStatus(response.body.result);
return {
courseId,
importJobId: response.body.result,
importJobResult
};
}
catch (e) {
// TODO : handle "409 conflict, courseId exists and mayCreateNewVersion is false" ?
throw new ScormClientError(e);
}
});
}
/**
* Check the status of a course import. This can be called incrementally to check the progress of a call to any
* of the import options. Note: The import job ID is only valid for one week after the course import finishes.
*
* [API Method - GetImportJobStatus](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/GetImportJobStatus)
*
* @param jobId The import job ID
* @param options Options
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.ImportJobResult}, or `undefined` if no job result was not found
*/
getCourseImportStatus(jobId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!jobId) {
throw new ScormClientError('No jobId provided');
}
try {
return (yield superagent_1.default
.get(`${BASE_PATH}/courses/importJobs/${jobId}`)
.set('Authorization', this.getBearerString(options))).body;
}
catch (e) {
if (HttpStatus.notFound(e)) {
return undefined;
}
throw new ScormClientError(e);
}
});
}
/**
* Updates the title of the course.
*
* [API Method - SetCourseTitle](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/SetCourseTitle)
*
* @param courseId The course ID
* @param title The new title to be set
* @param options Options
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered,
* or if the referenced course does not exist
* @returns A {@link types.SuccessIndicator}
*/
setCourseTitle(courseId, title, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
if (!title) {
throw new ScormClientError('No title provided');
}
try {
const response = yield superagent_1.default
.put(`${BASE_PATH}/courses/${courseId}/title`)
.set('Authorization', this.getBearerString(options))
.send({ title });
const success = HttpStatus.isSuccess(response);
return {
success,
message: success ? '' : `Failed to set course title '${courseId}'`
};
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* Deletes the specified course. When a course is deleted, so is everything connected to the course. This includes:
* - Registrations
* - Invitations
* - Dispatches
* - Debug Logs
*
* [API Method - DeleteCourse](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/DeleteCourse)
*
* @param courseId The course ID
* @param options Options
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered,
* or if the referenced course does not exist
* @returns A {@link types.SuccessIndicator}
*/
deleteCourse(courseId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
try {
const response = yield superagent_1.default
.delete(`${BASE_PATH}/courses/${courseId}`)
.set('Authorization', this.getBearerString(options));
const success = HttpStatus.isSuccess(response);
return {
success,
message: success ? '' : `Failed to delete course '${courseId}'`
};
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* TODO: Test this function to see what happens with non-existent courses and/or course versions and confirm what
* is return and what is thrown. Update the function to handle scenarios appropriately ...
*
* Returns information about all versions of the course. This can be useful to see information such as registration
* counts and modification times across the versions of a course.
*
* [API Method - GetCourseVersions](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/GetCourseVersions)
*
* @param courseId The course ID
* @param options CourseVersionFetchOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered,
* @returns An array of {@link types.Course}, or an empty array if no courses were found. This method does not
* support pagination, all versions will be returned
*/
getCourseVersions(courseId, options = {}) {
var _a, _b, _c, _d;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
try {
const query = {
since: (_a = options.since) !== null && _a !== void 0 ? _a : undefined,
until: (_b = options.until) !== null && _b !== void 0 ? _b : undefined,
includeCourseMetadata: (_c = options.includeCourseMetadata) !== null && _c !== void 0 ? _c : undefined,
includeRegistrationCount: (_d = options.includeRegistrationCount) !== null && _d !== void 0 ? _d : undefined
};
const response = yield superagent_1.default
.get(`${BASE_PATH}/courses/${courseId}/versions`)
.set('Authorization', this.getBearerString(options))
.query(query);
return response.body.courses || [];
}
catch (e) {
if (HttpStatus.notFound(e)) {
throw new ScormClientError(e, 'Bad request, likely due to invalid query options', 400);
}
throw new ScormClientError(e);
}
});
}
/**
* Deletes the specified version of the course. If deleting the last remaining version of the course,
* the course itself will be deleted and no longer accessible. When a course is deleted, so is everything
* connected to the course. This includes:
* - Registrations
* - Invitations
* - Dispatches
* - Debug Logs
*
* [API Method - DeleteCourseVersion](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/DeleteCourseVersion)
*
* @param courseId The course ID
* @param versionNumber The version number
* @param options Options
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered,
* or if the referenced course does not exist
* @returns A {@link types.SuccessIndicator}
*/
deleteCourseVersion(courseId, versionNumber, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
if (!versionNumber) {
throw new ScormClientError('No versionNumber provided');
}
try {
const response = yield superagent_1.default
.delete(`${BASE_PATH}/courses/${courseId}/versions/${versionNumber}`)
.set('Authorization', this.getBearerString(options));
const success = HttpStatus.isSuccess(response);
return {
success,
message: success ? '' : `Failed to delete course version '${courseId}:${versionNumber}'`
};
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* Creates or updates an asset file uploaded from your file system into the course version. The file will be sent
* as part of the request and will be stored in SCORM Cloud alongside the course. This is a useful way to modify
* the course structure without needing to reimport the whole course after you've made changes.
*
* If the course structure is being heavily modified, consider creating a new version instead. This can be done by
* calling {@link importCourse}, while passing true for mayCreateNewVersion
*
* [API Method - UploadCourseVersionAssetFile](https://cloud.scorm.com/docs/v2/reference/swagger/#/course/UploadCourseVersionAssetFile)
*
* @param courseId The course ID
* @param versionNumber The version number
* @param filePath The local path to the file that must be imported to SCORM Cloud
* @param destinationPath The relative path from the course's base directory on SCORM Cloud, to where the asset file will be uploaded
* @param options CourseVersionAssetUploadOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.CourseVersionAssetUploadResponse} if successfull
*/
uploadCourseVersionAssetFile(courseId, versionNumber, filePath, destinationPath, options = {}) {
var _a;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
if (!versionNumber) {
throw new ScormClientError('No versionNumber provided');
}
if (!filePath) {
throw new ScormClientError('No filePath provided');
}
if (!destinationPath) {
throw new ScormClientError('No destinationPath provided');
}
try {
const query = {
updateAssetPolicy: (_a = options.updateAssetPolicy) !== null && _a !== void 0 ? _a : undefined
};
const response = yield superagent_1.default
.post(`${BASE_PATH}/courses/${courseId}/versions/${versionNumber}/asset/upload`).type('form')
.set('Authorization', this.getBearerString(options))
.query(query)
.send({ destination: destinationPath })
.attach('file', filePath);
const success = HttpStatus.isSuccess(response);
const file = success && response.body.filename ? response.body.filename : '';
const destination = success && response.body.destination ? response.body.destination : '';
const successMessage = success && file
? `File '${file}' uploaded to '${destination}'`
: 'File uploaded';
return {
success,
message: success ? successMessage : `Failed to set upload asset for course '${courseId}'`
};
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* Creates a new registration. Registrations are the billable unit in SCORM Cloud, and represents the link between
* a learner and a course. A registration will contain a few pieces of information such as learner identifiers,
* the id of the course being registered for, and several other optional fields.
*
* A registration must be tied to a specific course at creation time. When the registration is "launched"
* (see {@link createLaunchLink}), the course specified at creation time will be launched.
*
* [API Method - CreateRegistration](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/CreateRegistration)
*
* @param registrationId An ID for this registration
* @param courseId The course ID for which the learner should be registered
* @param learner The details of the learner, at minimum a learner ID should be specified
* @param options RegistrationOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered,
* or if the referenced course or learner does not exist, or if creating a duplicate registration
* @returns A {@link types.SuccessIndicator}
*/
createRegistration(registrationId, courseId, learner, options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!registrationId) {
throw new ScormClientError('No registrationId provided');
}
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
if (!learner) {
throw new ScormClientError('No learner details provided');
}
try {
const query = {
courseVersion: (_a = options.courseVersion) !== null && _a !== void 0 ? _a : undefined
};
const registration = {
courseId,
learner,
registrationId,
xapiRegistrationId: (_b = options.xapiRegistrationId) !== null && _b !== void 0 ? _b : undefined,
learnerTags: (_c = options.learnerTags) !== null && _c !== void 0 ? _c : undefined,
courseTags: (_d = options.courseTags) !== null && _d !== void 0 ? _d : undefined,
registrationTags: (_e = options.registrationTags) !== null && _e !== void 0 ? _e : undefined,
postBack: (_f = options.postBack) !== null && _f !== void 0 ? _f : undefined,
initialRegistrationState: (_g = options.initialRegistrationState) !== null && _g !== void 0 ? _g : undefined,
initialSettings: (_h = options.initialSettings) !== null && _h !== void 0 ? _h : undefined
};
const response = yield superagent_1.default
.post(`${BASE_PATH}/registrations`)
.set('Authorization', this.getBearerString(options))
.query(query)
.send(registration);
const success = HttpStatus.isSuccess(response);
return {
success,
message: success ? '' : `Failed to create registration '${registrationId}'`
};
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* Checks that the registration exists within SCORM Cloud. No registration data will be returned for this call.
* If you are looking for information about the registration, try using {@link getRegistration} instead.
*
* [API Method - GetRegistration](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/GetRegistration)
*
* @param registrationId An ID for which registration to check
* @param options Options
* @throws {@link ScormClientError} if an invalid request was made
* @returns A boolean true or false
*/
registrationExists(registrationId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!registrationId) {
throw new ScormClientError('No registrationId provided');
}
try {
const response = yield superagent_1.default
.head(`${BASE_PATH}/registrations/${registrationId}`)
.set('Authorization', this.getBearerString(options));
if (HttpStatus.isSuccess(response)) {
return true;
}
if (HttpStatus.notFound(response)) {
return false;
}
throw new ScormClientError(`Bad request: ${response.status}`);
}
catch (e) {
if (HttpStatus.isSuccess(e)) {
return true;
}
if (HttpStatus.notFound(e)) {
return false;
}
throw new ScormClientError(e);
}
});
}
/**
* Deletes the specified registration. This will also delete all instances of the registration.
*
* [API Method - DeleteRegistration](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/DeleteRegistration)
*
* @param registrationId The registration ID
* @param options Options
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered,
* or if the referenced registration does not exist
* @returns A {@link types.SuccessIndicator}
*/
deleteRegistration(registrationId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!registrationId) {
throw new ScormClientError('No registrationId provided');
}
try {
const response = yield superagent_1.default
.delete(`${BASE_PATH}/registrations/${registrationId}`)
.set('Authorization', this.getBearerString(options));
const success = HttpStatus.isSuccess(response);
return {
success,
message: success ? '' : `Failed to delete registration '${registrationId}'`
};
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* Returns detailed information about the registration. This includes completion status, time taken, score,
* and pass/fail status.
*
* [API Method - GetRegistrationProgress](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/GetRegistrationProgress)
*
* @param registrationId The registration ID for which to return progress
* @param options RegistrationFetchOptions
*/
getRegistration(registrationId, options = {}) {
var _a, _b, _c;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!registrationId) {
throw new ScormClientError('No registrationId provided');
}
try {
const query = {
includeChildResults: (_a = options.includeChildResults) !== null && _a !== void 0 ? _a : undefined,
includeInteractionsAndObjectives: (_b = options.includeInteractionsAndObjectives) !== null && _b !== void 0 ? _b : undefined,
includeRuntime: (_c = options.includeRuntime) !== null && _c !== void 0 ? _c : undefined
};
return (yield superagent_1.default
.get(`${BASE_PATH}/registrations/${registrationId}`)
.set('Authorization', this.getBearerString(options))
.query(query)).body;
}
catch (e) {
throw new ScormClientError(e);
}
});
}
/**
* This is an overloaded method for {@link getRegistration}, and is exactly the same thing. It exists purely to provide a
* method named in similar fashion to the official API.
*
* [API Method - GetRegistrationProgress](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/GetRegistrationProgress)
*
* @param registrationId The registration ID for which to return progress
* @param options RegistrationFetchOptions
*/
getRegistrationProgress(registrationId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.getRegistration(registrationId, options);
});
}
/**
* Returns a list of registrations for the given course. Can be filtered to provide a subset of results.
*
* This request is paginated and will only provide a limited amount of resources at a time. If there are more
* results to be collected, a more token provided with the response which can be passed to get the next page
* of results. When passing this token, no other filter parameters can be sent as part of the request. The
* resources will continue to respect the filters passed in by the original request.
*
* [API Method - GetRegistrations](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/GetRegistrations)
*
* @param courseId The course ID for which to return registrations
* @param options RegistrationQueryOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.RegistrationQueryResponse} that has a property named `registrations`, which is an array
* of type {@link types.Registration}, or an empty array if no registrations were found. It also includes a
* '`more`' property which, if present, can be used to retrieve the next paginated set of results
*/
getRegistrationsForCourse(courseId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
if (!courseId) {
throw new ScormClientError('No courseId provided');
}
return yield this.getRegistrations(Object.assign({ courseId }, options));
});
}
/**
* Returns a list of registrations for the given learner. Can be filtered to provide a subset of results.
*
* This request is paginated and will only provide a limited amount of resources at a time. If there are more
* results to be collected, a more token provided with the response which can be passed to get the next page
* of results. When passing this token, no other filter parameters can be sent as part of the request. The
* resources will continue to respect the filters passed in by the original request.
*
* [API Method - GetRegistrations](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/GetRegistrations)
*
* @param learnerId The learner ID for which to return registrations
* @param options RegistrationQueryOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.RegistrationQueryResponse} that has a property named `registrations`, which is an array
* of type {@link types.Registration}, or an empty array if no registrations were found. It also includes a
* '`more`' property which, if present, can be used to retrieve the next paginated set of results
*/
getRegistrationsForLearner(learnerId, options = {}) {
return __awaiter(this, void 0, void 0, function* () {
if (!learnerId) {
throw new ScormClientError('No learnerId provided');
}
return yield this.getRegistrations(Object.assign({ learnerId }, options));
});
}
/**
* Returns a list of registrations. Can be filtered to provide a subset of results.
*
* This request is paginated and will only provide a limited amount of resources at a time. If there are more
* results to be collected, a more token provided with the response which can be passed to get the next page
* of results. When passing this token, no other filter parameters can be sent as part of the request. The
* resources will continue to respect the filters passed in by the original request.
*
* [API Method - GetRegistrations](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/GetRegistrations)
*
* @param options RegistrationQueryOptions
* @throws {@link ScormClientError} if an invalid request was made, or an error encountered
* @returns A {@link types.RegistrationQueryResponse} that has a property named `registrations`, which is an array
* of type {@link types.Registration}, or an empty array if no registrations were found. It also includes a
* '`more`' property which, if present, can be used to retrieve the next paginated set of results
*/
getRegistrations(options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
try {
const query = {
courseId: (_a = options.courseId) !== null && _a !== void 0 ? _a : undefined,
learnerId: (_b = options.learnerId) !== null && _b !== void 0 ? _b : undefined,
since: (_c = options.since) !== null && _c !== void 0 ? _c : undefined,
until: (_d = options.until) !== null && _d !== void 0 ? _d : undefined,
datetimeFilter: (_e = options.datetimeFilter) !== null && _e !== void 0 ? _e : undefined,
tags: (_f = options.tags) !== null && _f !== void 0 ? _f : undefined,
filter: (_g = options.filter) !== null && _g !== void 0 ? _g : undefined,
filterBy: (_h = options.filterBy) !== null && _h !== void 0 ? _h : undefined,
orderBy: (_j = options.orderBy) !== null && _j !== void 0 ? _j : undefined,
more: (_k = options.more) !== null && _k !== void 0 ? _k : undefined,
includeChildResults: (_l = options.includeChildResults) !== null && _l !== void 0 ? _l : undefined,
includeInteractionsAndObjectives: (_m = options.includeInteractionsAndObjectives) !== null && _m !== void 0 ? _m : undefined,
includeRuntime: (_o = options.includeRuntime) !== null && _o !== void 0 ? _o : undefined
};
return (yield superagent_1.default
.get(`${BASE_PATH}/registrations`)
.set('Authorization', this.getBearerString(options))
.query(query)).body;
}
catch (e) {
if (HttpStatus.notFound(e)) {
throw new ScormClientError(e, 'Bad request, likely due to invalid query options', 400);
}
throw new ScormClientError(e);
}
});
}
/**
* Get a launch link, which is a relatively short lived url, used to launch the course for a given registration.
* Launch links are meant as a way to provide access to your content. When a learner visits the link, the course
* will be launched and registration progress will start to be tracked.
*
* [API Method - BuildRegistrationLaunchLink](https://cloud.scorm.com/docs/v2/reference/swagger/#/registration/BuildRegistrationLaunchLink)
*
* @param registrationId The registration ID for which to create and return a launch link
* @param redirectOnExitUrl The URL the application should redirect to when the learner exits or completes a course.
* Alternatively one of the following keywords can be used:
* - closer : A page that automatically tries to close the browser tab/window
* - blank : A blank page
* - message : A page with a message about the course being complete
*
* If an invalid url is specified, the Message.html page will be loaded
* @param options LaunchLinkOptions
*/
createLaunchLink(registrationId, redirectOnExitUrl, options = {}) {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
return __awaiter(this, void 0, void 0, function* () {
yield this.authorise(options);
if (!registrationId) {
throw new ScormClientError('No registrationId provided');
}
if (!redirectOnExitUrl) {
throw new ScormClientError('No redirectOnExitUrl provided');
}
try {
const query = {
redirectOnExitUrl,
expiry: (_a = options.expiry) !== null && _a !== void 0 ? _a : undefined,
tracking: (_b = options.tracking) !== null && _b !== void 0 ? _b : undefined,
startSco: (_c = options.startSco) !== null && _c !== void 0 ? _c : undefined,
culture: (_d = options.culture) !== null && _d !== void 0 ? _d : undefined,
cssUrl: (_e = options.cssUrl) !== null && _e !== void 0 ? _e : undefined,
learnerTags: (_f = options.learnerTags) !== null && _f !== void 0 ? _f : undefined,
courseTags: (_g = options.courseTags) !== null && _g !== void 0 ? _g : undefined,
registrationTags: (_h = options.registrationTags) !== null && _h !== void 0 ? _h : undefined,
additionalvalues: (_j = options.additionalvalues) !== null && _j !== void 0 ? _j : undefined,
launchAuth: (_k = options.launchAuth) !== null && _k !== void 0 ? _k : undefi