Skip to content

Creating a custom adapter

An adapter is used to allow your system to interface programmatically with Canvas. This section includes information on how to build and install a custom adapter.

Example Adapter

An example adapter is provided along with the Canvas source code and can be used as a starting point. Please see the readme included in the source code to understand the functionality enabled in the example adapter and how to install and use it. The example adapter is meant to be a kernel of an implementation, not necessarily production ready (depending on your requirements). For example, additional optimizations would be necessary to enable larger scale implementations (i.e. dozens of VENs and/or hundreds of resources).

Creating and installing an adapter

Create gem

In this document, custom text chosen by the implementor is placed in brackets: <replace-with-your-text>, where the brackets are also replaced along with the text.

Helpful Commands

  • rvm list - list installed ruby versions and display which is being used
  • rvm use <ruby-version> - select version of ruby to use
  • rvm gemset list - list gemsets and which is being used
  • rvm use gemset <gemset-name-here> - select which gemset to use

Install Ruby

A gem is started like you start a new rails project.

Install the desired version of ruby using rvm:

rvm install ruby-version

Prepare gemset

Create a gemset for the gem, and select the gemset for use using rvm:

rvm gemset create <gemset-name-here>
rvm gemset use <gemset-name-here>

Install the desired version of rails into the gemset:

gem install rails -v <version-#>

Generate And Setup gem

Generate the gem using rails:

rails plugin new <gem_name_here> --full

Modify /lib//engine.rb to look as follows:

module <GemNameHere>
  class Engine < ::Rails::Engine
    config.autoload_paths += Dir["#{config.root}/lib/**/"]
    config.eager_load_paths += Dir["#{config.root}/lib/**/"]

    initializer :append_migrations do |app|
      unless app.root.to_s.match root.to_s
        config.paths["db/migrate"].expanded.each do |expanded_path|
          app.config.paths["db/migrate"] << expanded_path

Create custom api file /lib/api/ that has the following structure:

module Api
  module <GemNameHere>
    SCHEMA = {
        custom_function1: { validate: false },
        custom_function2: { validate: false }

    def self.custom_function1(payload)
      # custom implementation 

    def self.custom_function2(payload)
      # custom implementation 

Modify /lib/ to look as follows:

require "<gem_name_here>/engine"

module <GemNameHere>
  if defined?(Rails)
    require "<gem_name_here>/engine"
    require "api/<custom_file_name_here>"

Note that the extension is not needed on .

Edit gemspec

The "TODO" values in / will need to be edited before installing the gem in an application.

Install gem In Vtn

The vtn looks in vtn-root/../local_oadr_gems for local gems. Create this directory if it does not exist. The most flexible way to add gems to the directory is by creating symlinks to each desired gem on the filesystem. This way, a gem can be removed by removing the symink and not needing to move or delete the real gem. If you create a symlink to a gem, make sure the name of the symlink is the same name as the root directory of the gem.

For example, the file structure should look as follow:

  --> canvas_adapter

When a gem has been added to the vtn-root/../local_oadr_gems directory, install it as you would install other gems in vtn. From vtn-root, run:

bundle install

NOTE: If you are developing the gem and then installing in the vtn, keep in mind which gemset is currently being used. If switching to the vtn, you can cd out of the vtn root, and then back in, to allow rvm to select the correct gemset. If you switch back to developing the gem, you will need to use rvm use <gemset-name-here> to select the correct gemset.

Authentication & API

Authentication is available via the API Controller built into Canvas.

API Access must be enabled for a user, or the requests will be rejected. On the users account page in the Canvas UI, there is a check box to enable API access for that account.

In addition, requests may only be made for the namespace that the account, or they will be rejected. The namespace is specified in the API request path.

The VTN's built in API Controller supports two types of Authentication, Basic and JWT. The example adapter uses this for authentication - see the example adapter source code and readme file for more information on how these are implemented and how to use them.


Remote IDs

Targets, VENs and Events all have a remote_id field that can be use to link records in the external system. It is a string field limited to 255 characters. Remote IDs can be updated in the UI.

Finding VEN targets by name

VEN targets (to add a VEN to an Event) are given IDs that aren't exposed for modification through the UI, but is field is synced with associated VEN and available to query in the following format: VEN:.

For example, given a ven named TH_VEN, the VEN ID target can be retrieved with

target = Target.find_by_name("VEN:TH_VEN")

The example adapter provides an endpoint to convert between different types of IDs (e.g. for VENs, ids, names and remote IDs)