Changing data from a plug-in
The i2 Notebook Web API provides the ability to write data to the chart, either by adding new records or by editing or deleting existing records. The API also allows you to change which records on the chart are selected, and to modify the view state of the chart.
All changes to the chart take place asynchronously, and you must perform them using transactions to ensure that they do not conflict with changes made by other plug-ins or users.
In the i2 Notebook Web API, you use mutations for this purpose. Mutations are transactions that run as a single atomic action, and contain one or more operations that make changes to chart data or application state.
When asked to start a mutation transaction, the application calls a mutation handler that you provide, which receives the following parameters:
A parameter of type
IApplicationContents
, which provides read access to the visual and record data for the chart, as well as to its schema.A parameter that implements one of the
...Mutations
interfaces, which provides access to mutation operations that allow changes to record and application state.
Commit and rollback
Just like database transactions, when your mutation handler finishes, it must indicate whether the mutation operations that it created should be "committed" and applied to the chart and application, or "rolled back" and not applied.
Committing a mutation causes operations in the handler to be applied to the chart. Some operations, such as selection and view changes, are applied immediately. Other operations, such as writing record data, are sent to the server before control returns to the application.
Mutation reports
When you commit or roll back a mutation, you can opt to provide information that is displayed to the user as a notification pop-up. The ISuccessReport
and IDetailedReport
define objects that you can return from mutation handlers.
Note: The fate of the transaction (commit or rollback) and the report that the user sees (success, failure, or nothing) are independent from each other. Reporting an error to the user is often associated with rolling back the mutation transaction, but you can combine a transaction commit with an error report, or a rollback with a success report.
i2 Notebook adheres to two standard design behaviors - demonstrated most obviously in the built-in Expand functionality - that provide a helpful and unobtrusive user experience when you follow them in your own plug-ins:
It's not always necessary to provide an explicit success report to the user. For a command that adds data to the chart, the success of the operation is often obvious from the fact that data has been added to the chart. Only provide an explicit "success" report if there is something else of value to report to the user.
If a command is supposed to add data to the chart but after starting the mutation you discover that there is no (new) data to add, then roll back the transaction and provide an explicit report to the user to indicate that there was nothing to add.
(In these circumstances, reporting nothing to the user would leave them uncertain whether the command even ran, harming their confidence in the application and your plug-in.)
Deciding whether "nothing was added" is a "success" or a "failure" will depend on factors such as whether the lack of new data is a normal situation or caused by an error.
Tracked and untracked mutations
i2 Notebook supports two different types of mutation:
Tracked mutations, which users can undo, and which you start by calling
IApplication.runTrackedMutations()
.Your tracked mutation handler receives an
ITrackedMutations
object, which provides a full set of chart mutation operations.A committed, tracked mutation collects all of its operations into a single entry in the application undo stack. A user can undo all of the changes from a tracked mutation with a single undo action.
Untracked mutations, which users cannot undo, and which you start by calling
IApplication.runUntrackedMutations()
.Your untracked mutation handler receives an
IUntrackedMutations
object, which provides a more restricted set of chart mutation operations (specifically, chart record selection, and chart view position and zoom level).The operations in an untracked mutation take place without creating an entry in the application undo stack.
If your command only affects selection or the view of the chart, then consider implementing it with untracked mutations. For all other commands, use tracked mutations.
Cancelation and status messages
If you implement a mutation handler as an asynchronous function, the handler can be canceled through the ICancelation
object that it receives. Triggering the cancelation automatically rolls back the mutation.
While a mutation handler is running, the application can show status messages that your handler provides by using the status
property of the ITrackedMutations
or IUntrackedMutations
interface. For example:
// Use an asynchronous mutation handler so that we can wait for the external data.
// During the fetch, the application shows a spinner, a message, and a cancel button.
getToolViewApi().runTrackedMutations(async (application, mutations, cancelation) => {
do {
// Fetch some external data, passing in the cancelation signal so that the fetch
// is aborted if the signal is. After each fetch, the external resource returns
// an object that indicates whether there's more data to fetch.
const response = await fetch('http://some-external-service.com', { signal: cancelation.signal });
const responseJSON = response.json();
// Update the application progress message.
mutations.status.setMessage('Fetching data');
// We've got the data, so we can create some mutation commands with it.
mutations.addEntityRecord(...);
// Only carry on with the loop if the signal is not aborted.
} while (responseJSON.moreDataToFetch && !cancelation.signal.aborted);
// If the signal is aborted, roll back mutations and show a notification.
if (cancelation.signal.aborted) {
return {
type: 'rollback',
report: {
type: 'warning',
title: 'Aborted',
details: 'Data fetch aborted'
}
}
}
return {
type: 'commit',
actionDisplayName: 'Fetch data'
}
});