rustici-software-cloud-v2

Swagger Generated JavaScript Client for SCORM Cloud API v2

Usage no npm install needed!

<script type="module">
  import rusticiSoftwareCloudV2 from 'https://cdn.skypack.dev/rustici-software-cloud-v2';
</script>

README

rustici-software-cloud-v2

RusticiSoftwareCloudV2 - JavaScript client for rustici-software-cloud-v2 REST API used for SCORM Cloud integrations. This SDK is automatically generated by the Swagger Codegen project:

  • API version: 2.0
  • Package version: 2.0.0
  • Build package: io.swagger.codegen.languages.JavascriptClientCodegen

Installation

npm

rustici-software-cloud-v2

npm install rustici-software-cloud-v2 --save

GitHub

rustici-software-cloud-v2

npm install RusticiSoftware/scormcloud-api-v2-client-javascript --save

Local

To use the library locally without pulling from the npm registry, first install the dependencies by changing into the directory containing package.json (and this README). Let's call this JAVASCRIPT_CLIENT_DIR. Then run:

npm install

Next, link it globally in npm with the following, also from JAVASCRIPT_CLIENT_DIR:

npm link

Finally, switch to the directory you want to use your rustici-software-cloud-v2 from, and run:

npm link /path/to/<JAVASCRIPT_CLIENT_DIR>

You should now be able to require('rustici-software-cloud-v2') in javascript files from the directory you ran the last command above from.

For browser

The library also works in the browser environment via npm and browserify. After following the above steps and installing browserify with npm install -g browserify, perform the following (assuming main.js is your entry file, that's to say your javascript file where you actually use this library):

browserify main.js > bundle.js

Then include bundle.js in the HTML pages.

Webpack Configuration

Using Webpack you may encounter the following error: "Module not found: Error: Cannot resolve module", most certainly you should disable AMD loader. Add/merge the following section to your webpack config:

module: {
    rules: [
        {
            parser: {
                amd: false
            }
        }
    ]
}

Tips and Tricks

Working with headers will require passing a response parameter of the callback function. This allows for grabbing the header directly from the response object:

// Note: This code is specifically designed to not modify any existing data
const dispatchApi = new ScormCloud.DispatchApi();
dispatchApi.updateDispatches(new ScormCloud.UpdateDispatchSchema(), { "since": new Date().toISOString() }, function(err, data, response) {
    console.log(response.headers["x-total-count"]);
});

Release 2.0.X:

  • The return type for endpoints which used to return a File have changed to instead return a Blob object. This change allows both Node and the browser to download the files appropriately.

Check the changelog for further details of what has changed.

Sample Code

There are additional dependencies required to get the sample code running.

npm install browser-or-node prompt-sync
const ScormCloud = require('rustici-software-cloud-v2');
const fs = require('fs');

let prompt;
var jsEnv = require("browser-or-node");
if (jsEnv.isBrowser) {
    prompt = window.prompt;
}
if (jsEnv.isNode) {
    prompt = require('prompt-sync')({sigint: true});
}


// ScormCloud API credentials
// Note: These are not the same credentials used to log in to ScormCloud
const APP_ID = "APP_ID";
const SECRET_KEY = "SECRET_KEY";

// Sample values for data
const COURSE_PATH = "/PATH/TO/COURSE/RunTimeAdvancedCalls_SCORM20043rdEdition.zip";
let COURSE_FILE;

const COURSE_ID = "JS_SAMPLE_COURSE";
const LEARNER_ID = "JS_SAMPLE_COURSE_LEARNER";
const REGISTRATION_ID = "JS_SAMPLE_COURSE_REGISTRATION";

// String used for output formatting
const OUTPUT_BORDER = "---------------------------------------------------------\n";


/**
 * This sample will consist of:
 * 1. Creating a course.
 * 2. Registering a learner for the course.
 * 3. Building a link for the learner to take the course.
 * 4. Getting the learner's progress after having taken the course.
 * 5. Viewing all courses and registrations.
 * 6. Deleting all of the data created via this sample.
 *
 * All input variables used in this sample are defined up above.
 */
function main() {
    // Configure HTTP basic authorization: APP_NORMAL
    const APP_NORMAL = ScormCloud.ApiClient.instance.authentications['APP_NORMAL'];
    APP_NORMAL.username = APP_ID;
    APP_NORMAL.password = SECRET_KEY;

    // Create a course
    createCourse(COURSE_ID, COURSE_FILE, function(courseDetails) {
        // Show details of the newly imported course
        console.log("Newly Imported Course Details: ");
        console.log(courseDetails);

        // Create a registration for the course
        createRegistration(COURSE_ID, LEARNER_ID, REGISTRATION_ID, function() {
            // Create the registration launch link
            buildLaunchLink(REGISTRATION_ID, function(launchLink) {
                // Show the launch link
                console.log(OUTPUT_BORDER);
                console.log(`Launck Link: ${launchLink}`);
                console.log("Navigate to the url above to take the course. " +
                    (jsEnv.isNode ? "Hit enter once complete." : "Click OK on the in-browser prompt once complete."));
                prompt();

                // Get the results for the registration
                getResultForRegistration(REGISTRATION_ID, function(registrationProgress) {
                    // Show details of the registration progress
                    console.log(OUTPUT_BORDER);
                    console.log("Registration Progress: ");
                    console.log(registrationProgress);

                    // Get information about all the courses in ScormCloud
                    getAllCourses(function(courseList) {
                        // Show details of the courses
                        console.log(OUTPUT_BORDER);
                        console.log("Course List: ");
                        courseList.forEach((course) => {
                            console.log(course);
                        });

                        // Get information about all the registrations in ScormCloud
                        getAllRegistrations(function(registrationList) {
                            // Show details of the registrations
                            console.log(OUTPUT_BORDER);
                            console.log("Registration List: ");
                            registrationList.forEach((registration) => {
                                console.log(registration);
                            });

                            // Delete all the data created by this sample
                            cleanUp(COURSE_ID, REGISTRATION_ID);
                        });
                    });
                });
            });
        });
    });
}

function logErrorAndCleanUp(error) {
    console.error(error)

    // Delete all the data created by this sample
    cleanUp(COURSE_ID, REGISTRATION_ID);
}

/**
 * Sets the default OAuth token passed with all calls to the API.
 *
 * If a token is created with limited scope (i.e. read:registration),
 * calls that require a different permission set will error. Either a
 * new token needs to be generated with the correct scope, or the
 * default access token can be reset to None. This would cause the
 * request to be made with basic auth credentials (appId/ secret key)
 * instead.
 *
 * Additionally, you could create a new configuration object and set
 * the token on that object instead of the default access token. This
 * configuration would then be passed into the Api object:
 *
 * apiClient = new ScormCloud.ApiClient();
 * tokenRequest = {
 *     permissions: { scopes: [ "write:course", "read:course" ] },
 *     expiry: new Date((new Date()).getTime() + 2 * 60 * 1000).toISOString()
 * };
 * appManagementApi.createToken(tokenRequest, function (error, data) {
 *     if (error) {
 *         return logErrorAndCleanUp(error.response.text);
 *     }
 *     apiClient.authentications['OAUTH'].accessToken = data.result;
 *     courseApi = new ScormCloud.CourseApi(apiClient);
 *
 *     // Logic would go here
 * });
 *
 * Any calls that would use this CourseApi instance would then have the
 * write:course and read:course permissions passed automatically, but
 * other instances would be unaffected and continue to use other means
 * of authorization.
 *
 * @param {Array<String>} scopes List of permissions for calls made with the token.
 */
function configureOAuth(scopes, callback) {
    const appManagementApi = new ScormCloud.ApplicationManagementApi();

    // Set permissions and expiry time of the token
    // The expiry expected for token request must be in ISO-8601 format
    const expiry = new Date((new Date()).getTime() + 2 * 60 * 1000).toISOString();
    const permissions = { scopes: scopes };

    // Make the request to get the OAuth token
    tokenRequest = { permissions: permissions, expiry: expiry };
    appManagementApi.createToken(tokenRequest, function (error, data) {
        if (error) {
            return logErrorAndCleanUp(error.response.text);
        }

        // Set the default access token used with further API requests.
        // To remove the token, reset
        // ScormCloud.ApiClient.instance.authentications['OAUTH'].accessToken
        // back to null before the next call.
        const OAUTH = ScormCloud.ApiClient.instance.authentications['OAUTH'];
        OAUTH.accessToken = data.result;

        callback();
    });
}

/**
 * Creates a course by uploading the course from your local machine.
 * Courses are a package of content for a learner to consume.
 *
 * Other methods for importing a course exist. Check the documentation
 * for additional ways of importing a course.
 *
 * @param {String} courseId Id that will be used to identify the course.
 * @param {File} courseFile The File object containing the course contents.
 * @returns {CourseSchema} Detailed information about the newly uploaded course.
 */
function createCourse(courseId, courseFile, callback) {
    function createCourseLogic() {
        const courseApi = new ScormCloud.CourseApi();
        let jobId;

        // This call will use OAuth with the "write:course" scope
        // if configured.  Otherwise the basic auth credentials will be used
        courseApi.createUploadAndImportCourseJob(courseId, { file: courseFile }, function(error, data) {
            if (error) {
                return logErrorAndCleanUp(error.response.text);
            }

            jobId = data.result;

            function getUploadStatus(jobId) {
                // This call will use OAuth with the "read:course" scope
                // if configured.  Otherwise the basic auth credentials will be used
                let interval = setInterval(function () {
                    courseApi.getImportJobStatus(jobId, function(error, data) {
                        if (error) {
                            clearInterval(interval);
                            return logErrorAndCleanUp(error.response.text);
                        }

                        if (data.status == ScormCloud.ImportJobResultSchema.StatusEnum.RUNNING) {
                            return;
                        }

                        // If importing has finished (failure or success)
                        clearInterval(interval);

                        if (data.status == ScormCloud.ImportJobResultSchema.StatusEnum.ERROR) {
                            return logErrorAndCleanUp("Course is not properly formatted: " + data.message);
                        }

                        callback(data.importResult.course);
                    });
                }, 1000);
            }
            getUploadStatus(jobId);
        });
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "write:course", "read:course" ], createCourseLogic);
    createCourseLogic();
}

/**
 * Creates a registration allowing the learner to consume the course
 * content. A registration is the link between a learner and a single
 * course.
 *
 * @param {String} courseId Id of the course to register the learner for.
 * @param {String} learnerId Id that will be used to identify the learner.
 * @param {string} registrationId Id that will be used to identify the registration.
 */
function createRegistration(courseId, learnerId, registrationId, callback) {
    function createRegistrationLogic() {
        const registrationApi = new ScormCloud.RegistrationApi();
        const learner = { id: learnerId };
        const registration = { courseId: courseId, learner: learner, registrationId: registrationId };
        registrationApi.createRegistration(registration, {}, function(error) {
            if (error) {
                return logErrorAndCleanUp(error.response.text);
            }

            callback();
        });
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "write:registration" ], createRegistrationLogic);
    createRegistrationLogic();
}

/**
 * Builds a url allowing the learner to access the course.
 *
 * This sample will build the launch link and print it out. It will then
 * pause and wait for user input, allowing you to navigate to the course
 * to generate sample learner progress. Once this step has been reached,
 * hitting the enter key will continue program execution.
 *
 * @param {String} registrationId Id of the registration the link is being built for.
 * @returns {String} Link for the learner to launch the course.
 */
function buildLaunchLink(registrationId, callback) {
    function buildLaunchLinkLogic() {
        const registrationApi = new ScormCloud.RegistrationApi();
        const settings = { redirectOnExitUrl: "Message" };
        registrationApi.buildRegistrationLaunchLink(registrationId, settings, function(error, data) {
            if (error) {
                return logErrorAndCleanUp(error.response.text);
            }

            callback(data.launchLink);
        });
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "read:registration" ], buildLaunchLinkLogic);
    buildLaunchLinkLogic();
}

/**
 * Gets information about the progress of the registration.
 *
 * For the most up-to-date results, you should implement our postback
 * mechanism. The basic premise is that any update to the registration
 * would cause us to send the updated results to your system.
 *
 * More details can be found in the documentation:
 * https://cloud.scorm.com/docs/v2/guides/postback/
 *
 * @param {String} registrationId Id of the registration to get results for.
 * @returns {RegistrationSchema} Detailed information about the registration's progress.
 */
function getResultForRegistration(registrationId, callback) {
    function getResultForRegistrationLogic() {
        const registrationApi = new ScormCloud.RegistrationApi();
        registrationApi.getRegistrationProgress(registrationId, {}, function(error, data) {
            if (error) {
                return logErrorAndCleanUp(error.response.text);
            }

            callback(data);
        });
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "read:registration" ], getResultForRegistrationLogic);
    getResultForRegistrationLogic();
}

/**
 * Gets information about all courses. The result received from the API
 * call is a paginated list, meaning that additional calls are required
 * to retrieve all the information from the API. This has already been
 * accounted for in the sample.
 *
 * @returns {Array<CourseSchema>} List of detailed information about all of the courses.
 */
function getAllCourses(callback) {
    function getAllCoursesLogic() {
        const courseApi = new ScormCloud.CourseApi();
        const courseList = [];

        function getPaginatedCourses(more) {
            // This call is paginated, with a token provided if more results exist.
            // Additional filters can be provided to this call to get a subset
            // of all courses.
            courseApi.getCourses({ more: more },  function(error, data) {
                if (error) {
                    return logErrorAndCleanUp(error.response.text);
                }

                courseList.push(...data.courses);

                if (data.more) {
                    return getPaginatedCourses(data.more);
                }

                callback(courseList);
            });
        }
        getPaginatedCourses(null);
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "read:course" ], getAllCoursesLogic);
    getAllCoursesLogic();
}

/**
 * Gets information about the registration progress for all
 * registrations. The result received from the API call is a paginated
 * list, meaning that additional calls are required to retrieve all the
 * information from the API. This has already been accounted for in the
 * sample.
 *
 * This call can be quite time-consuming and tedious with lots of
 * registrations. If you find yourself making lots of calls to this
 * endpoint, it might be worthwhile to look into registration postbacks.
 *
 * More details can be found in the documentation:
 * https://cloud.scorm.com/docs/v2/guides/postback/
 *
 * @returns {Array<RegistrationSchema>} List of detailed information about all of the registrations.
 */
function getAllRegistrations(callback) {
    function getAllRegistrationsLogic() {
        const registrationApi = new ScormCloud.RegistrationApi();
        const registrationList = [];

        function getPaginatedRegistrations(more) {
            // This call is paginated, with a token provided if more results exist.
            // Additional filters can be provided to this call to get a subset
            // of all registrations.
            registrationApi.getRegistrations({ more: more },  function(error, data) {
                if (error) {
                    return logErrorAndCleanUp(error.response.text);
                }

                registrationList.push(...data.registrations);

                if (data.more) {
                    return getPaginatedRegistrations(data.more);
                }

                callback(registrationList);
            });
        }
        getPaginatedRegistrations(null);
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "read:registration" ], getAllRegistrationsLogic);
    getAllRegistrationsLogic();
}

/**
 * Deletes all of the data generated by this sample.
 * This code is run even if the program has errored out, providing a
 * "clean slate" for every run of this sample.
 *
 * It is not necessary to delete registrations if the course
 * they belong to has been deleted. Deleting the course will
 * automatically queue deletion of all registrations associated with
 * the course. There will be a delay between when the course is deleted
 * and when the registrations for the course have been removed. The
 * registration deletion has been handled here to prevent scenarios
 * where the registration hasn't been deleted yet by the time the
 * sample has been rerun.
 *
 * @param {String} courseId Id of the course to delete.
 * @param {String} registrationId Id of the registration to delete.
 */
function cleanUp(courseId, registrationId) {
    function cleanUpLogic() {
        // This call will use OAuth with the "delete:course" scope
        // if configured.  Otherwise the basic auth credentials will be used
        const courseApi = new ScormCloud.CourseApi();
        courseApi.deleteCourse(courseId, function(error) {
            if (error) {
                throw error;
            }
        });

        // The code below is to prevent race conditions if the
        // sample is run in quick successions.

        // This call will use OAuth with the "delete:registration" scope
        // if configured.  Otherwise the basic auth credentials will be used
        const registrationApi = new ScormCloud.RegistrationApi();
        registrationApi.deleteRegistration(registrationId, function(error) {
            if (error) {
                throw error;
            }
        });
    }

    // (Optional) Further authenticate via OAuth token access
    // First line is with OAuth, second is without
    // configureOAuth([ "delete:course", "delete:registration" ], cleanUpLogic);
    cleanUpLogic();
}

// If running through the browser, call browserFileUpload instead:

// <!DOCTYPE html>
// <html>
//     <body>
//         <p>
//             When running the sample in the browser, a File object is required to be passed to the sample code.
//             Input a file using the input below and then run the sample code.
//         </p>
//         <input id="fileButton" type=file />
//     </br>
//         <button onclick="runSample()">Click here to run sample code in the console.</button>
//         <p id="runLog"></p>
//     </body>
// </html>
// <script src="bundle.js"></script>
// <script>
//     function runSample() {
//         const file = document.getElementById('fileButton').files[0];
//         browserFileUpload(file);
//
//         document.getElementById("runLog").innerHTML += "Sample is running, please see console for output. The process may take a few seconds. <br>";
//     }
// </script>

if (jsEnv.isNode) {
    COURSE_FILE = fs.createReadStream(COURSE_PATH);
    main();
} else {
    window.browserFileUpload = (file) => {
        COURSE_FILE = file;
        main();
    };
}