Skip to content

Latest commit

 

History

History
338 lines (261 loc) · 10.8 KB

File metadata and controls

338 lines (261 loc) · 10.8 KB

Resource Provider plugins

This document should be read in conjunction with SERVERPLUGINS.md as it contains additional information regarding the development of plugins that facilitate the storage and retrieval of resource data.


Overview

The SignalK specification defines the path /signalk/v1/api/resources for accessing resources to aid in navigation and operation of the vessel.

It also defines the schema for the following Common resource types:

  • routes
  • waypoints
  • notes
  • regions
  • charts

each with its own path under the root resources path (e.g. /signalk/v1/api/resources/routes).

It should also be noted that the /signalk/v1/api/resources path can also host other types of resource data which can be grouped within a Custom path name (e.g. /signalk/v1/api/resources/fishingZones).

The SignalK server does not natively provide the ability to store or retrieve resource data for either Common and Custom resource types. This functionality needs to be provided by one or more server plugins that handle the data for specific resource types.

These plugins are called Resource Providers.

This de-coupling of resource request handling and storage / retrieval provides great flexibility to ensure that an appropriate resource storage solution can be configured for your SignalK implementation.

SignalK server handles requests for both Common and Custom resource types in a similar manner, the only difference being that it does not perform any validation on Custom resource data, so a plugin can act a s a provider for both types.


Server Operation:

The Signal K server handles all requests to /signalk/v1/api/resources and all sub-paths, before passing on the request to the registered resource provider plugin.

The following operations are performed by the server when a request is received:

  • Checks for a registered provider for the resource type
  • Checks that ResourceProvider methods are defined
  • For Common resource types, checks the validity of the:
    • Resource id
    • Submitted resource data.

Upon successful completion of these operations the request will then be passed to the registered resource provider plugin.


Resource Provider plugin:

For a plugin to be considered a Resource Provider it needs to implement the ResourceProvider interface.

By implementing this interface the plugin is able to register with the SignalK server the:

  • Resource types provided for by the plugin
  • Methods to used to action requests.

It is these methods that perform the retrival, saving and deletion of resources from storage.

Resource Provider Interface


The ResourceProvider interface defines the contract between the the Resource Provider plugin and the SignalK server and has the following definition (which it and other related types can be imported from @signalk/server-api):

interface ResourceProvider: {
  types: string[],
  methods: {
    listResources: (type:string, query: {[key:string]:any})=> Promise<any>
    getResource: (type:string, id:string)=> Promise<any>
    setResource: (type:string, id:string, value:{[key:string]:any})=> Promise<any>
    deleteResource: (type:string, id:string)=> Promise<any>
  }
}

where:

  • types: An array containing a list of resource types provided for by the plugin. These can be a mixture of both Common and Custom resource types.
  • methods: An object containing the methods resource requests are passed to by the SignalK server. The plugin MUST implement each method, even if that operation is not supported by the plugin!

Method Details:


listResources(type, query): This method is called when a request is made for resource entries of a specific resource type that match a specifiec criteria.

Note: It is the responsibility of the resource provider plugin to filter the resources returned as per the supplied query parameters.

type: String containing the type of resource to retrieve.

query: Object contining key | value pairs repesenting the parameters by which to filter the returned entries. e.g. {distance,'50000}

returns:

  • Resolved Promise containing a list of resource entries on completion.
  • Rejected Promise containing an Error if incomplete or not implemented.

Example resource request:

GET /signalk/v1/api/resources/waypoints?bbox=5.4,25.7,6.9,31.2&distance=30000

ResourceProvider method invocation:

listResources(
  'waypoints', 
  {
    bbox: '5.4,25.7,6.9,31.2',
    distance: 30000
  }
);

getResource(type, id): This method is called when a request is made for a specific resource entry of the supplied resource type and id.

type: String containing the type of resource to retrieve.

id: String containing the target resource entry id. e.g. 'urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99'

returns:

  • Resolved Promise containing the resource entry on completion.
  • Rejected Promise containing an Error if incomplete or not implemented.

Example resource request:

GET /signalk/v1/api/resources/routes/urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99

ResourceProvider method invocation:

getResource(
  'routes', 
  'urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99'
);

setResource(type, id, value): This method is called when a request is made to save / update a resource entry of the specified resource type, with the supplied id and data.

type: String containing the type of resource to store.

id: String containing the target resource entry id. e.g. 'urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99'

value: Resource data to be stored.

returns:

  • Resolved Promise containing a list of resource entries on completion.
  • Rejected Promise containing an Error if incomplete or not implemented.

Example PUT resource request:

PUT /signalk/v1/api/resources/routes/urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99 {resource_data}

ResourceProvider method invocation:

setResource(
  'routes', 
  'urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99',
  {<resource_data>}
);

Example POST resource request:

POST /signalk/v1/api/resources/routes {resource_data}

ResourceProvider method invocation:

setResource(
  'routes', 
  '<server_generated_id>',
  {<resource_data>}
);

deleteResource(type, id): This method is called when a request is made to remove the specific resource entry of the supplied resource type and id.

type: String containing the type of resource to delete.

id: String containing the target resource entry id. e.g. 'urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99'

returns:

  • Resolved Promise on completion.
  • Rejected Promise containing an Error if incomplete or not implemented.

Example resource request:

DELETE /signalk/v1/api/resources/routes/urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99

ResourceProvider method invocation:

deleteResource(
  'routes', 
  'urn:mrn:signalk:uuid:07894aba-f151-4099-aa4f-5e5773734b99'
);

Registering a Resource Provider:


To register the resource provider plugin with the SignalK server, the server's resourcesApi.register() function should be called during plugin startup.

The server resourcesApi.register() function has the following signature:

app.resourcesApi.register(pluginId: string, resourceProvider: ResourceProvider)

where:

  • pluginId: is the plugin's id
  • resourceProvider: is a reference to the plugins ResourceProvider interface.

Note: A resource type can only have one registered plugin, so if more than one plugin attempts to register as a provider for the same resource type, the first plugin to call the register() function will be registered by the server for the resource types defined in the ResourceProvider interface!

Example:

module.exports = function (app) {

  let plugin= {
    id: 'mypluginid',
    name: 'My Resource Providerplugin',
    resourceProvider: {
      types: ['routes','waypoints'],
      methods: {
        listResources: (type, params)=> { ... },
        getResource: (type:string, id:string)=> { ... } ,
        setResource: (type:string, id:string, value:any)=> { ... },
        deleteResource: (type:string, id:string)=> { ... }
      }
    }
  }

  plugin.start = function(options) {
    ...
    app.resourcesApi.register(plugin.id, plugin.resourceProvider);
  }
}

Un-registering the Resource Provider:


When a resource provider plugin is disabled, it should un-register itself to ensure resource requests are no longer directed to it by calling the SignalK server. This should be done by calling the server's resourcesApi.unRegister() function during shutdown.

The server resourcesApi.unRegister() function has the following signature:

app.resourcesApi.unRegister(pluginId: string)

where:

  • pluginId: is the plugin's id

Example:

module.exports = function (app) {

  let plugin= {
    id: 'mypluginid',
    name: 'My Resource Providerplugin',
    resourceProvider: {
      types: [ ... ],
      methods: { ... }
    }
  }

  plugin.stop = function(options) {
    app.resourcesApi.unRegister(plugin.id);
    ...
  }
}

Example:

Resource Provider plugin providing for the retrieval of routes & waypoints.

// SignalK server plugin 
module.exports = function (app) {

  let plugin= {
    id: 'mypluginid',
    name: 'My Resource Providerplugin',
    // ResourceProvider interface
    resourceProvider: {
      types: ['routes','waypoints'],
      methods: {
        listResources: (type, params)=> { 
          return new Promise( (resolve, reject) => { 
            // fetch resource entries from storage
            ....
            if(ok) { // success
              resolve({
                'id1': { ... },
                'id2': { ... },
              });
            } else { // error
              reject(new Error('Error encountered!')
            }
          }
        },
        getResource: (type, id)=> {
          // fetch resource entries from storage
          ....
          if(ok) { // success
            return Promise.resolve({
              ...
            }); 
          } else { // error
            reject(new Error('Error encountered!')
          }
        },
        setResource: (type, id, value)=> { 
          // not implemented
          return Promise.reject(new Error('NOT IMPLEMENTED!')); 
        },
        deleteResource: (type, id)=> {
          // not implemented
          return Promise.reject(new Error('NOT IMPLEMENTED!'));  
        }
      }
    },

    start: (options)=> { 
      ... 
      app.resourceApi.register(this.id, this.resourceProvider);
    },

    stop: ()=> { 
      app.resourceApi.unRegister(this.id);
      ... 
    }
  }
}