Home Manual Reference Source Repository

Migration Guide: Moving from v2 to v3

Migration Guide: Moving from v2 to v3

The gsf-javascript-client-sdk has undergone a number of changes for version 3.0. This version is significant due to the adoption of a new HTTP API for GSF. This means that if you upgrade your SDK to v3, you will also need to update your server(s) to GSF version 3.0. Ensure that the gsf-request-handler is enabled on the server before using version 3 of the SDK. There are a number of breaking changes for the SDK user as a result of switching to the new API.

The purpose of this guide is to help you transition your application's source code from v2 to v3 of the SDK.

Server Class

GSF.Server is now GSF.Client.

  • The server class has been renamed to 'Client'.
  • The 'ServerArgs' object is now referred to as 'ClientOptions'.

Client.APIRoot no longer defaults to 'ese'.

  • The default APIRoot has been removed to align with the new HTTP API.

Service Class

Service.info() no longer contains task list.

Service.taskInfoList() response changed.

Task Class

Task.info() response changed.

Task.submit() and Task.submitAndWait() options changed.

Job Class

Job.info() response changed.

  • See JobInfo below for more details.

Job event names changed. They are now the same as the Client events.

- Renamed 'Accepted' to 'JobAccepted'.
- Renamed 'Started' to 'JobStarted'.
- Renamed 'Progress' to 'JobProgress'.
- Renamed 'Completed' to 'JobCompleted'.
- Renamed 'Succeeded' to 'JobSucceeded'.
- Renamed 'Failed' to 'JobFailed'.

Job.wait() response changed.

Types

TaskInfo

  • Added 'serviceName'.
  • Renamed 'name' to 'taskName'.
  • Renamed '<parameter>.dataType' to '<parameter>.type'.
  • Renamed '<parameter>.defaultValue' to '<parameter>.default'.
  • Replaced 'parameters' with 'inputParameters' and 'outputParameters'.
  • Removed '<parameter>.direction'
    • New 'inputParameters' and 'outputParameters' arrays indicate direction in their name.
Example of TaskInfo in v2
{
    "name": "ISODATAClassification",
    "displayName": "ISODATA Classification",
    "description": "This task clusters pixels in a dataset based on statistics only, without requiring you to define training classes.",
    "parameters": {
        "INPUT_RASTER": {
            "name": "INPUT_RASTER",
            "parameterType": "required",
            "displayName": "Input Raster",
            "description": "Specify a raster on which to perform unsupervised classification.",
            "direction": "INPUT",
            "dataType:": "ENVIRASTER"
        },
        "OUTPUT_RASTER": {
            "name": "OUTPUT_RASTER",
            "parameterType": "required",
            "displayName": "Output Raster",
            "description": "This is a reference to the output classification raster of filetype ENVI.",
            "direction": "OUTPUT",
            "dataType:": "ENVIRASTER"
        }
    }
}
Example of TaskInfo in v3
{
    "taskName": "ISODATAClassification",
    "serviceName": "ENVI",
    "displayName": "ISODATA Classification",
    "description": "This task clusters pixels in a dataset based on statistics only, without requiring you to define training classes.",
    "inputParameters": [
        {
            "name": "INPUT_RASTER",
            "type": "ENVIRASTER",
            "required": true,
            "displayName": "Input Raster",
            "description": "Specify a raster on which to perform unsupervised classification."
        }
    ],
    "outputParameters": [
        {
            "name": "OUTPUT_RASTER",
            "type": "ENVIRASTER",
            "displayName": "Output Raster",
            "description": "This is a reference to the output classification raster of filetype ENVI.",
            "required": true
        }
    ]
}

For full documention see TaskInfo.

ServiceInfo

  • Removed 'tasks'.
    • Use Service.tasks() or Service.taskInfoList() to obtain task lists.
Example of ServiceInfo in v2
{
    "name": "ENVI",
    "description": "ENVI processing routines",
    "tasks": [
        "Task1",
        "Task2",
        "Task3"
    ]
}
Example of ServiceInfo in v3
{
    "name": "ENVI",
    "description": "ENVI processing routines"
}

For full documentation see ServiceInfo.

SubmitOptions

  • Added 'jobOptions' object.
    • 'route' was moved into the 'jobOptions' object.
    • Any additional processing directives will reside in the 'jobOptions' object.
  • Renamed 'parameters' to 'inputParameters'.
Example of SubmitOptions in v2
const submitOptions = {
  parameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: '/some/url'
    },
    INDEX: 'Normalized Difference Vegetation Index'
  },
  route: "ENVIRoute"
};
Example of SubmitOptions in v3
const submitOptions = {
  inputParameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: '/some/url'
    },
    INDEX: 'Normalized Difference Vegetation Index'
  },
  jobOptions: {
      route: "ENVIRoute"
  }
};

For full documentation see SubmitOptions.

JobInfo

  • Added 'jobStart'.
  • Added 'jobEnd'.
  • Added 'jobSubmitted'.
  • Added 'jobOptions'.
  • Renamed 'inputs' to 'inputParameters'.
  • Renamed 'jobErrorMessage' to 'jobError'.
  • Renamed 'results' to 'jobResults'.
  • Renamed 'jobProgressMessage' to 'jobMessage'.
  • Removed 'jobRoute'. This property is now in the 'jobOptions' object.
  • Removed 'jobStatusUrl'.
Example of JobInfo in v2
{
    "jobId": 3410,
    "jobStatus": "Succeeded",
    "jobStatusURL": "ese/jobs/3410/status",
    "jobProgress": 100,
    "jobProgressMessage": "Completed",
    "jobRoute": "default",
    "taskName": "SpectralIndex",
    "serviceName": "ENVI",
    "jobErrorMessage": "",
    "inputs": {
        "index": "Iron Oxide",
        "input_raster": {
            "url": "/some/url",
            "factory": "URLRaster"
        }
    },
    "results": {
        "OUTPUT_RASTER": {
            "url": "/some/url",
            "factory": "URLRaster",
            "auxiliary_url": [
                "/some/url"
            ]
        }
    },
    "messages": [{
            "type": "esriJobMessageTypeInformative",
            "description": "Submission Time: Mon Jan 29 2018 16:07:52 GMT-0700 (Mountain Standard Time)"
        },
        {
            "type": "esriJobMessageTypeInformative",
            "description": "Start Time: Mon Jan 29 2018 16:07:52 GMT-0700 (Mountain Standard Time)"
        },
        {
            "type": "esriJobMessageTypeInformative",
            "description": "End Time: Mon Jan 29 2018 16:08:01 GMT-0700 (Mountain Standard Time) (Elapsed Time: 8.818 seconds)"
        }
    ]
}
Example of JobInfo in v3
{
    "jobId": 3410,
    "jobProgress": 100,
    "jobMessage": "Completed",
    "jobStatus": "Succeeded",
    "taskName": "SpectralIndex",
    "serviceName": "ENVI",
    "inputParameters": {
        "index": "Iron Oxide",
        "input_raster": {
            "url": "/some/url",
            "factory": "URLRaster"
        }
    },
    "jobResults": {
        "OUTPUT_RASTER": {
            "best": {
                "url": "/some/url",
                "factory": "URLRaster",
                "auxiliary_url": [
                    "/some/url"
                ]
            },
            "ese-job-parameter-mapper": {
                "url": "/some/url",
                "factory": "URLRaster",
                "auxiliary_url": [
                    "/some/url"
                ]
            }
        }
    },
    "jobOptions": {
        "route": "default"
    },
    "jobSubmitted": "2018-01-29T23:07:52.992Z",
    "jobStart": "2018-01-29T23:07:52.995Z",
    "jobEnd": "2018-01-29T23:08:01.813Z"
}

For full documentation see JobInfo.

JobResults

  • Each parameter now contains all of the parameter mappings. The highest ranked mapping is set to the 'best' key.
Example of JobResults in v2
{
    "OUTPUT_RASTER": {
        "url": "/some/url",
        "factory": "URLRaster",
        "auxiliary_url": [
            "/some/url"
        ]
    }
}
Example of JobResults in v3
{
    "OUTPUT_RASTER": {
        "best": {
            "url": "/some/url",
            "factory": "URLRaster",
            "auxiliary_url": [
                "/some/url"
            ]
        },
        "ese-job-parameter-mapper": {
            "url": "/some/url",
            "factory": "URLRaster",
            "auxiliary_url": [
                "/some/url"
            ]
        }
    }
}

For full documentation see JobResults.

GSF JavaScript Client SDK

GSF JavaScript Client SDK

About the SDK

The GSF JavaScript Client SDK provides a client-side JavaScript library for interacting with GSF. The SDK works in both the browser and Node.js.

  1. This repository contains pre-built distributable files located in the /dist/ directory.
    • /dist/GSF-node.js - The Node.js bundle.
    • /dist/GSF-node.js.map - The Node.js bundle source map.
    • /dist/GSF.js - The non-minified web bundle.
    • /dist/GSF.js.map - The non-minified web bundle source map file.
    • /dist/GSF.min.js - The minified web bundle.
    • /dist/GSF.min.js.map - The minified web bundle source map file.

Basic Usage

Importing the SDK

Using ECMAScript 2015

  • Import everything with GSF namespace:

    import * as GSF from 'gsf-js-client-sdk';

  • Import specific classes:

    import { Job, Task } from 'gsf-js-client-sdk';

Using Node.js

  • Require the SDK:

    const GSF = require('gsf-js-client-sdk/dist/GSF-node');

Including the SDK with a Script Tag

  1. Include the GSF JavaScript Client SDK in your project. The example below assumes the SDK file is located next to your html file.

    <script src="GSF.min.js"></script>

  2. Access the SDK using the global GSF object.

    <script>console.log(GSF);</script>

  3. Below is a simple example of running a job and retrieving the results. You will need to update the server address and port below to reflect the server that you are using.

const myAddress = 'MyServer';
const myPort = 9191;

// GSF Client
const client = GSF.client({
    address: myAddress,
    port: myPort
  });

// Create a service object.
const service = client.service('ENVI');

// Create a task object.
const task = service.task('SpectralIndex');

const NDVIParameters = {
  inputParameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: `http://${myAddress}:${myPort}/ese/data/qb_boulder_msi`
    },
    INDEX: 'Normalized Difference Vegetation Index'
  }
};

// Submit a job.
task.submitAndWait(NDVIParameters).then((results) => {
    // Do something with results.
    AddToMap(results.OUTPUT_RASTER.best);
  }).catch((err) => {
    // Display error.
  });

Requirements

Server Sent Events

The GSF JavaScript Client SDK relies on server-sent events for communication with the server. Developers who wish to build apps that run on browsers lacking EventSource support will want to use a polyfill. This is not necessary when using the SDK in Node.js.

To view a list of the browsers that support EventSource please go here: https://caniuse.com/#search=eventsource

There are several polyfills available that provide implementations of the EventSource API. One such polyfill that is available from the npm and Bower package managers is called 'eventsource-polyfill'. For information on installation and usage, see https://github.com/amvtek/EventSource.

Building the SDK

  1. Clone the repository.

    $ git clone https://github.com/geospatial-services-framework/gsf-js-client-sdk

  2. From the root directory of the project install the dependencies.

    $ npm install

  3. Run the build script.

    $ npm run build

Testing the SDK

Using a Test Server

We have provided a simple mock server implementation that can be used for very basic testing of the SDK. There are several other test scripts available. See the scripts section of the package.json file for more information.

Run tests in the browser against mock server.

$ npm run test

Run tests in Node.js against mock server.

$ npm run test-node

Building the Documentation

  1. Build the documentation.

    $ npm run help

  2. View the generated help files located in the /help directory (on Windows).

    $ start help/index.html

Examples

Examples

Below are several examples of using the SDK with JavaScript. For TypeScript specific examples see the TypeScript example. Before using the SDK, it is also recommended that you read the best practices section.

List Available Services

The GSF Client object provides the ability to list the available services on the server.

// GSF Client
const client = GSF.client({
    address: 'MyServer',
    port: '9191'
  });

// Get an array of available services.
client.services().then((services) => {
  services.forEach((service) => {
    // Print each service name.
    console.log(service.name);
  });
}).catch((err) => {
  // There was an error.
});

List Available Tasks

The Service object provides the ability to list the available tasks on the service. The example below lists all tasks associated with the ENVI service.

// GSF Client
const client = GSF.client({
    address: 'MyServer',
    port: '9191'
  });

// Get the ENVI service.
const service = client.service('ENVI');

// Get an array of available tasks.
service.tasks().then((tasks) => {
  tasks.forEach((task) => {
    // Print each task name.
    console.log(task.name);
  });
}).catch((err) => {
  // There was an error.
});

Get Task Information

The Task object allows you to query the task and its parameters. This may be useful when dynamically constructing UI elements representing task input parameters.

// GSF Client
const client = GSF.client({
    address: 'MyServer',
    port: '9191'
  });

// Get the ENVI service.
const service = client.service('ENVI');

// Get a task.
const task = service.task('SpectralIndex');

task.info().then((info) => {
  // Print the task info.
  console.log(info);
}).catch((err) => {
  // There was an error.
});

Run a Task

There are many ways to run tasks and retrieve results using the GSF JavaScript SDK. The following examples assume you have completed the steps below to create a task object.

// Get a task.
const task = GSF.client({address:'MyServer',port:'9191'}).service('ENVI').task('SpectralIndex');

const taskParameters = {
  inputParameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: 'http://MyServer:9191/ese/data/qb_boulder_msi'
    },
    INDEX: 'Normalized Difference Vegetation Index'
  }
};

Using Promises

The SDK provides a Promise-based interface for submitting tasks. If the task succeeds, the promise will be fulfilled. If the job fails, the promise will be rejected. There are two ways to use promises for job submission.

1. Use .submit() and then .wait()

The Task.submit() function returns a Promise to a Job, and the Job.wait() function returns a Promise to the JobResults.

// Submit a job.
task.submit(taskParameters)
  .then(job => job.wait())
  .then((results) => {
    // Do something with results.
    // This function is an example and is not provided by the SDK.
    AddToMap(results.OUTPUT_RASTER.best);
  }).catch((jobErrorMessage) => {
    // Display error.
  });
2. Use .submitAndWait()

This function simply combines the .submit() and .wait() functions. This is perhaps the simplest way to submit a job and retrieve the results. Use this if you are only intereseted in results and do not wish to interact with the Job object.

// Submit a job.
task.submitAndWait(taskParameters).then((results) => {
    // Do something with results.
    // This function is an example and is not provided by the SDK.
    AddToMap(results.OUTPUT_RASTER.best);
  }).catch((jobErrorMessage) => {
    // Display error.
  });

Using Server Events

The Client and Job objects give you access to all job related events emitted by the server. These classes inheret from Node's EventEmitter and support methods such as .on(), .once(), .removeAllListeners(), etc. The following example shows how to listen for job events on the Client.

// GSF Client
const client = GSF.client({
    address: 'MyServer',
    port: '9191'
  });

// Set up an event listeners.
client.once('JobSucceeded', (data) => {
  console.log('Job Succeeded: ', data.jobId);
});

client.once('JobFailed', (data) => {
  console.log('Job Failed: ', data.jobId);
});

// Create a service object.
const service = client.service('ENVI');

// Create a task object.
const task = service.task('SpectralIndex');

const NDVIParameters = {
  inputParameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: 'http://MyServer:9191/ese/data/qb_boulder_msi'
    },
    INDEX: 'Normalized Difference Vegetation Index'
  }
};

// Submit a job.
task.submit(NDVIParameters);

For a complete list of available events, see the Client class documentation.

Tracking Job Progress

There are two ways to track the progress of a job.

Progress Callbacks

The .submit() and .submitAndWait() functions support the inclusion of a progress callback for reporting job progress.

const progressCallback = function (data) {
  console.log('Job progress percent: ', data.progress);
  console.log('Job progress message: ', data.message);
};

// Submit a job.
task.submitAndWait(parameters, progressCallback).then((results) => {
    // Do something with results.
    // This function is an example and is not provided by the SDK.
    AddToMap(results.OUTPUT_RASTER.best);
  }).catch((jobErrorMessage) => {
    // Display error.
  });

Progress Events

Job progress events are emitted by the Client and Job objects.

Client Progress Events

It is possible to listen to all job progress events emitted by the server using the Client object.

// GSF Client
const client = GSF.client({
    address: 'MyServer',
    port: '9191'
  });

// Set up an event listeners.
client.on('JobProgress', (data) => {
  console.log('Job: ', data.jobId, ' progress percent: ', data.progress);
  console.log('Job: ', data.jobId, ' progress message: ', data.message);
});

// Create a service object.
const service = client.service('ENVI');

// Create a task object.
const task = service.task('SpectralIndex');

const NDVIParameters = {
  inputParameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: 'http://MyServer:9191/ese/data/qb_boulder_msi'
    },
    INDEX: 'Normalized Difference Vegetation Index'
  }
};

// Submit a job.
task.submit(NDVIParameters);

Job Progress Events

A Job object emits progress events for the particular job which it represents.

// GSF Client
const client = GSF.client({
    address: 'MyServer',
    port: '9191'
  });

// Create a service object.
const service = client.service('ENVI');

// Create a task object.
const task = service.task('SpectralIndex');

const NDVIParameters = {
  inputParameters: {
    INPUT_RASTER: {
      FACTORY: 'URLRaster',
      URL: 'http://MyServer:9191/ese/data/qb_boulder_msi'
    },
    INDEX: 'Normalized Difference Vegetation Index'
  }
};

// Submit a job.
task.submit(NDVIParameters).then((job) => {
  // Set up an event listener on the job.
  job.on('JobProgress', (data) => {
    console.log('Job ', data.jobId, ' progress percent: ', data.progress);
    console.log('Job ', data.jobId, ' progress message: ', data.message);
  });
}).catch((jobErrorMessage) => {
  // Display error.
});

Cancelling Jobs

Below is an example of cancelling a job based on its job ID.

const myJobId = 1;
const job = new client.job(myJobId);
const force = false; // Do not force cancel.

// Cancel Job
job.cancel(force).then(() => {
  console.log('Cancel has been successfully requested.');
}).catch((err) => {
  // Display error.
});

Typescript Example

Below is an example of submitting a job using typescript.

import * as GSF from 'gsf-js-client-sdk';

// Get a task.
const clientOptions: GSF.ClientOptions = {address: 'MyServer', port: '9191'};
const myClient: GSF.Client = GSF.client(clientOptions);
const ENVIService: GSF.Service = myClient.service('ENVI');
const myTask: GSF.Task = ENVIService.task('SpectralIndex');

const taskParameters: GSF.SubmitOptions = {
    inputParameters: {
        INPUT_RASTER: {
            FACTORY: 'URLRaster',
            URL: 'http://MyServer:9191/ese/data/qb_boulder_msi'
        },
        INDEX: 'Normalized Difference Vegetation Index'
    }
};

// Submit a job.
task.submitAndWait(taskParameters)
    .then((results: GSF.JobResults) => {
        // Do something with results.
        // This function is an example and is not provided by the SDK.
        AddToMap(results.OUTPUT_RASTER.best);
    }).catch((jobErrorMessage) => {
        // Display error.
    });

Best Practices

Connecting to Servers

The examples throughout this documentation explain various concepts within the SDK using complete examples. Most of the examples create a new Client object for every example. This is to ensure a fully functional and self-contained example but is not a good practice when developing web apps. It is recommended that you limit the number of Client objects that you create. Ideally, your app will create only one instance of this class and pass the reference around where needed. This helps ensure consistency and prevent the possibility of exceeding the browser's per-domain connection limit.

References

References

Class Summary

Static Public Class Summary
public

The Client class is used to connect to the server and retrieve information about available services and jobs.

public

Job

The Job class is used for job operations.

public

The Service class is used to inspect and create tasks for a service.

public

The Task class is used to submit and inspect tasks.

Typedef Summary

Static Public Typedef Summary
public

The ClientOptions object contains information about the server.

public

The GSF object provides an entry point to the SDK.

public

The InputParameter object contains information about an input parameter.

public

Emitted when a job is accepted.

public

Emitted when a job completes.

public

Emitted when a job fails.

public

The JobInfo object contains information about a job.

public

Filtering options for listing jobs.

public

The Job Options object contains processing options to be used when running the job.

public

Emitted when job progress is updated.

public

Information about job progress.

public

The job output results.

public

The JobResults object contains the job results.

public

Emitted when a job starts.

public

Called when a job starts processing.

public

Emitted when a job succeeds.

public

Provides information about the node on which the job ran.

public

The OutputParameter object contains information about an output parameter.

public

The ServiceInfo object contains information about a service.

public

The Submit Options object contains the information needed to run a job.

public

The TaskInfo object contains information about a task.

Change Log

Change Log

All notable changes to this project will be documented in this file.

3.0.0 / 2018-03-21

New Features

  • Updated the SDK to use the new GSF HTTP API.

Breaking Changes

  • Several breaking changes were made for this release. For a detailed summary of the changes, see the Migration Guide in the documentation.

2.2.0 / 2018-03-22

New Features

  • Added Server.jobInfoList() for fetching an array of the current JobInfo objects.

Bug Fixes

  • Fix #10 - Server.jobInfoList() will retrieve full JobInfo objects from the server.

2.1.0 / 2018-03-21

New Features

  • Added headers object to the ServerArgs object to allow custom headers to be used in requests.

2.0.0 / 2017-07-24

New Features

  • Added the ability to use the SDK in Node.js. See documentation for more details.

Breaking Changes

  • Task class: Replaced 'server' and 'serviceName' constructor arguments with 'service'. This only affects Task objects created using the Task class constructor; there is no change to Task objects created using the Service.Task() method. See API documentation for more details.

1.0.0 / 2017-02-26

New Features

  • Add a change log.