Project Context
The Project Context module is a key part of the Customer Success module framework which establishes a container for project dependencies and a system for other modules to await certain dependencies before completing their own initialization process.
The Project Context Object
When included in a project, the Project Context module will establish an object in the global context at projects.<The Orbita project ID>:
The project context object is established with the following properties and methods:
The object is actually a proxy that prevents overwriting the base properties and assists with notifying subscribers awaiting dependencies when those dependencies have been initialized.
_revision
The experience designer flow revision ID at the time the project context was created, used to determine staleness.
isStale (hidden, established by proxy)
isStale will be true if the context is considered stale (determined by comparing the context revision ID to the current experience designer revision ID), false otherwise.
id
The ID of the Orbita project that the context belongs to.
host
The current Orbita environment host.
environmentName
The current Orbita environment name (the first segment of the host).
version
The version of Orbita that the environment is running on.
tier
The assumed tier (“dev”, “stage”, or “prod”) based on the presence of the word “sandbox” or a suffix of “-dev” or “-stage” in the host.
awaitDependencies
Parameters:
dependencies (string or array of strings): One or more keys of dependencies to await
Returns: A promise that resolves when all awaited dependencies have been initialized.
Getting the Project Context
Modules and other project code dependent on the project context should obtain it upon initialization using the following code:
const orbitaUtil = global.get("orbitaUtil");
const projectId = orbitaUtil.getProjectId(node);
const establishDeferral = () => {
let deferred = null;
const promise = new Promise((resolve, reject) => {
deferred = {
resolve,
reject
};
});
promise.deferred = deferred;
return promise;
};
let projects = global.get("projects");
if (!projects) {
projects = {};
global.set("projects", projects);
}
flow.set("project", undefined);
let project = projects[projectId];
if (!project || project.isStale) {
project = establishDeferral();
projects[projectId] = project;
}
Promise.resolve(project).then((resolvedProject) => {
flow.set("project", resolvedProject); // For ease of access
node.send(msg); // Not required if no subsequent code dependent on the context
});
The code above does the following:
Check if the projects object exists in the global context
If not, create it
Clear any current reference to the project context in the flow context
Check for an existing project context that is not stale
If none is available, create a deferred promise as a placeholder for a new project context
Await the project context (this will execute immediately if there is an existing project that is not stale). Once it is available:
Set a new reference to the project context in the flow context (for ease of access)
Invoke any subsequent nodes
In the Project Context module, there is corresponding code:
let projects = global.get("projects");
if (!projects) {
projects = {};
global.set("projects", projects);
}
const projectId = orbitaUtil.getProjectId(node);
let existing = projects[projectId];
if (existing && existing.deferred) {
const deferral = existing.deferred;
const newContext = createContext(projectId);
projects[projectId] = newContext;
deferral.resolve(newContext);
} else if (!existing || existing.isStale) {
projects[projectId] = createContext(projectId);
}
This code does the following:
Check if the projects object exists in the global context
If not, create it
Check for a deferred promise placeholder
If one exists, create a new project context, replace the placeholder with it, and resolve the deferred promise
If there is no placeholder, and there is either no existing context, or the existing context is stale, create a new project context
Awaiting Dependencies
After getting the project context, dependencies can be awaited with code such as:
const project = flow.get("project");
project.awaitDependencies(["settings", "utilities", "errorHandling"]).then((resolvedDependencies) => {
node.send(msg);
}).catch((error) => {
node.error(`<MODULE NAME>: Dependency resolution failed: ${error}`);
});
This code does the following:
Get the project context from the reference in the flow context.
Call awaitDependencies, passing it the names of any dependencies required
Once those dependencies are available, the promise returned by awaitDependencies will be resolved with an object containing references to all resolved dependencies (references will also be added to the project context object).
Invoke any subsequent nodes (which presumably contain code dependent on the awaited dependencies)
Initializing Dependencies
Dependencies are initialized simply by setting a property with the module key on the project context object:
When initializing a new module, it’s a best practice to freeze the module object to avoid any unexpected modifications.
When awaitDependencies is called, if the dependency does not already exist, a deferred promise is established as a placeholder (similar to the process when the project context itself is initialized). Whenever a property is set on the project context object, a check is done for any such placeholders, which are resolved at that time.