Jump to: navigation, search

Orion/Documentation/Developer Guide/Long running operations in services and plugins

Long running operations in services and plugins

Methods in Orion plugins may run asychronusly returning a promise (see orion/Deferred.js). The result of the method should be returned by calling deferred.resolve or deferred.reject methods. If you wish to report progress you should use deferred.progress method. Progress information will be reported on UI if argument passed to progress method describes an operation, with API as follows:

Operation Description

type
String one of: loadstart, progress, error, abort, load, loadend
timestamp
Long the time when the operation war run
lengthComputable
Boolean true if the progress is deterministic
loaded
Integer Optional applicable if lengthComputable is true, number of items loaded/process
total
Integer Optional applicable if lengthComputable is true, total number of items to load/process
Location
URL Optional the location of task where we can find more details and perform operations like update.
Important! this attribute should not be present if this operation status is only important when current page is opened. If operation should be tracked also outside of the current page, for instance in All Operations page plugin provider is required to provide Operation Service.
expires
Long timestamp to which the operation will be available under given location
cancelable
Boolean Optional true if operation can be cancelled. Setting this field to true requires enabling reaction on deferred.cancel() each time we return deferred representing given operation.

Deferred Representing Operation

To handle operation as long running it should be returned as a deferred or promise (see orion/Deferred.js). This is the contract how service/plugin should handle the deferred:

deferred.progress(progress)
Optional should be called each time operation information changes. The progress should contain #Operation Description. If the progress method is never called the method invocation will be still handled properly, but the operation progress will not be displayed.
deferred.resolve(result)
should be called when operation is finished correctly to return the result of the operation.
deferred.reject(error)
should be called when operation failed. Error attribute should contain the description of error. Error may be returned in plain text or as a Json object containing fields
Message brief description of the problem
DetailedMessage detailed description of the problem
Severity Optional "Warning" or "Error"
onError(error)
function passed as a second argument to then or a third argument to when. This function needs to be implemented when cancelable option is turned on in operation description. Function should check if error.canceled attribute is set, and based on it handle the operation cancellation:
  1. deferred.then(null, function(error){
  2. 	if(error.canceled){
  3. 		cancelOperation();
  4. 	}
  5. });

Operation Service

Service methods

getOperation(location)
Returns promise that should act the same way as the one that started the operation. When operation is running updates in format of Operation Description should be returned by deferred.progress. When operation is finished returned promise should be resolved or rejected containing operation result. If operation was finished before getOperation was called the returned promise should be resolved straight away.
removeCompletedOperations()
Removes all completed operations tracked by this Operation Service, returns an array of locations of operations that are left.
removeOperation(location)
Removed operation represented by this location.

Service properties

name
String Name of the service
pattern
String or URI the base URI for Operations location or the pattern that should match the location. Based on this property services will be matched with given operation location.

Examples

Adding "UPPER" action to editor via long running operation without operation service:

  1. var provider = new orion.PluginProvider();
  2. var serviceImpl = {
  3.     run: function (text) {
  4.         var deferred = new orion.Deferred();
  5.         var timestamp = new Date().getTime();
  6.         var operationFinished = false;
  7.         setTimeout(function () {
  8.             deferred.resolve(text.toUpperCase());
  9.             operationFinished = true;
  10.         }, 5000);
  11.  
  12.         function reportProgress() {
  13.             if (operationFinished)
  14.                 return;
  15.             deferred.progress({
  16.                 type: "progress",
  17.                 timestamp: timestamp
  18.             });
  19.             setTimeout(reportProgress, 1000);
  20.         }
  21.  
  22.         reportProgress();
  23.  
  24.         return deferred.promise;
  25.     }
  26. };
  27. var serviceProps = {
  28.     name: "UPPERCASE",
  29.     img: "../images/gear.gif",
  30.     key: ["u", true]
  31. };
  32. provider.registerService("orion.edit.command", serviceImpl, serviceProps);
  33. provider.connect();

The same example, but with persistent operations that can be viewed on All Operations page. Operations persistence based on localStorage.

  1. var provider = new orion.PluginProvider();
  2.  
  3. var base = "myOperations:";
  4.  
  5. function saveOperation(operation) {
  6.     var operations = JSON.parse(localStorage.getItem(base) || "{}");
  7.     operations[operation.Location] = operation;
  8.     localStorage.setItem(base, JSON.stringify(operations));
  9. }
  10.  
  11. function generateLocation() {
  12.     var operationNumber = 1 + parseInt(localStorage.getItem("lastOperation") || "0");
  13.     localStorage.setItem("lastOperation", operationNumber);
  14.     return base + operationNumber;
  15. }
  16.  
  17. function reportProgress(deferred, operation) {
  18.     if (operation.type !== "load" && operation.type !== "progress")
  19.         return;
  20.     operation.type = "progress";
  21.     saveOperation(operation);
  22.     deferred.progress(operation);
  23.     setTimeout(function () {
  24.         reportProgress(deferred, operation)
  25.     }, 1000);
  26. }
  27. provider.registerService("orion.edit.command", {
  28.     run: function (text) {
  29.         var deferred = new orion.Deferred();
  30.         var timestamp = new Date().getTime();
  31.         var operation = {
  32.             type: "load",
  33.             timestamp: timestamp,
  34.             Location: generateLocation()
  35.         };
  36.         saveOperation(operation);
  37.         setTimeout(function () {
  38.             deferred.resolve(text.toUpperCase());
  39.             operation.type = "loadend";
  40.             saveOperation(operation);
  41.         }, 5000);
  42.  
  43.         reportProgress(deferred, operation);
  44.         return deferred.promise;
  45.     }
  46. }, {
  47.     name: "UPPERCASE",
  48.     img: "../images/gear.gif",
  49.     key: ["u", true]
  50. });
  51.  
  52. provider.registerService("orion.core.operation", {
  53.     getOperation: function (location) {
  54.         var deferred = new orion.Deferred();
  55.         var operation = JSON.parse(localStorage.getItem(base) || "{}")[location];
  56.         if (!operation) {
  57.             deferred.reject("Cannot find operation: " + location);
  58.             return deferred.promise;
  59.         }
  60.         if (operation.type !== "load" && operation.type !== "progress") {
  61.             deferred.resolve();
  62.         }
  63.         reportProgress(deferred, operation);
  64.         return deferred.promise;
  65.     },
  66.     removeCompletedOperations: function () {
  67.         var operations = JSON.parse(localStorage.getItem(base) || "{}");
  68.         for (var operationLocation in operations) {
  69.             var operation = operations[operationLocation];
  70.             if (operation.type !== "load" && operation.type !== "progress") {
  71.                 delete operations[operationLocation];
  72.             }
  73.         }
  74.         localStorage.setItem(base, JSON.stringify(operations));
  75.         return Object.keys(operations);
  76.     },
  77.     removeOperation: function (location) {
  78.         var operations = JSON.parse(localStorage.getItem(base) || "{}");
  79.         delete operations[location];
  80.     }
  81. }, {
  82.     name: "Upper Tasks",
  83.     pattern: base
  84. });
  85. provider.connect();