UNPKG

scormcloud-client

Version:

A Typescript and JS client for interfacing with the ScormCloud API

1,036 lines (1,035 loc) 50.5 kB
"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