ari-client

repository·master·Indexed 43 days ago

https://github.com/asterisk/node-ari-client

A Node.js client library for interacting with the Asterisk REST Interface (ARI), providing a specialized API layer on top of swagger-js. Version 2.2.0.

Tokens
11.3K
Snippets
39
Records
50
Agent score
5%

What's inside ari-client

  1. Overview of node-ari-client

    master
    The node-ari-client is a Node.js client library designed for the Asterisk REST Interface (ARI). It is built on top of swagger-js but provides an improved, Asterisk-specific API layer to make interacting with Asterisk more intuitive than using the raw swagger-generated API.
  2. How resources and operations work in node-ari-client

    master

    The client organizes functionality into two main patterns:

    1. Resource Collections: Properties on the ari object (like ari.bridges, ari.channels, ari.endpoints) expose operations to manage lists of resources or specific resources via IDs.

      • Example: ari.bridges.list() or ari.bridges.get({bridgeId: '...'}).
    2. Resource Instances: When you obtain a specific resource instance (e.g., a bridge object), you can call operations directly on it without re-specifying the ID.

      • Example: bridge.addChannel({channel: '...'}) is equivalent to ari.bridges.addChannel({bridgeId: '...', channel: '...'}).
    3. Resource Creation: You can instantiate local objects using factory functions like ari.Bridge(), ari.Channel(), ari.Playback(), or ari.LiveRecording(). These objects are not yet active in ARI until you call .create() on them.

    // Pattern 1: Collection access
    ari.bridges.list().then(function (bridges) {});
    
    // Pattern 2: Instance access
    bridge.addChannel({channel: 'uniqueid'});
    
    // Pattern 3: Creation via factory
    var bridge = ari.Bridge();
    bridge.create().then(function (bridge) {});
  3. Register and manage event listeners

    master

    Events can be registered at the Client level (receives all events of a type) or the Resource Instance level (receives events only for that specific instance, e.g., a specific Channel).

    Memory Management

    To prevent memory leaks, you must unregister event handlers when they are no longer needed. Handlers cannot be garbage collected until removed.

    • removeListener(eventName, handler): Removes a specific handler.
    • removeAllListeners(eventName): Removes all handlers for a specific event type.
    • once(eventName, handler): Registers a handler that automatically unregisters after firing once.
    // Client level event
    var handler = function (event, channel) {};
    ari.on('ChannelDtmfReceived', handler);
    ari.removeListener('ChannelDtmfReceived', handler);
    
    // Instance level event
    var channel = ari.Channel();
    channel.on('StasisStart', function (event, channelInstance) {
        // Only called for this specific channel
    });
    
    // One-time event
    var playback = ari.Playback();
    playback.once('PlaybackFinished', function (event, playback) {});
  4. How to mock ARI for unit testing

    master

    When writing unit tests for ari-client, you can mock components as follows:

    • ARI Resources: Use grunt genfixtures to generate fixtures from a local Asterisk instance.
    • REST Calls: Use the hock library to mock individual calls to the mocked ARI REST server.
    • Websocket Events: Create a websocket server and call its send method to simulate incoming events.
    • Helpers: Use the helpers module to create both a mocked ARI server and a mocked websocket server.
  5. Create and configure resources before activation

    master

    A powerful pattern in this client is creating a resource instance locally before it exists in ARI. This allows you to register event listeners on the object before the resource is actually created, ensuring you don't miss early events.

    To use this pattern:

    1. Call the factory function (e.g., ari.Channel()).
    2. Attach event listeners using .on('EventName', callback).
    3. Call .create() or an operation like .originate() to instantiate the resource in ARI.
    var channel = ari.Channel();
    
    // Register listeners before creation
    channel.on('StasisStart', function (event, channel) {});
    channel.on('ChannelDtmfReceived', function (event, channel) {});
    
    // Now create/originate the resource
    channel.originate(
        {endpoint: 'PJSIP/1000', app: 'application', appArgs: 'dialed'}
    ).then(function (channel) {
        // channel is now the updated instance from ARI
    });
  6. Set up a development environment

    master

    To develop on ari-client, clone the repository and follow these steps to install dependencies and link the package:

    1. Install all development dependencies:
    $ npm install
    $ npm link
    1. Run linting (jshint) and tests (mocha):
    $ npm test
    1. Generate a test coverage report (this also ensures coverage thresholds are met):
    $ npm run check-coverage
    1. Generate unit test fixtures from a local Asterisk instance:
    $ grunt genfixtures
    1. Generate developer documentation using JSDoc:
    $ grunt jsdoc
  7. How the Client and its resources work together

    master

    The Client acts as a central hub for all ARI interactions. It uses a two-step initialization process:

    1. API Attachment: During connect(), the client fetches the ARI resources.json via Swagger. It then dynamically attaches resource managers (like channels) and resource creators (like channel()) to itself.
    2. Resource Access: Once attached, you access resources as properties of the client. For example, client.channels provides access to channel-related operations.

    Resource Creators: The client also attaches 'creator' functions. If a resource type is known, you can call client[resourceType](id, values) to create a local instance of that resource with a specific ID and initial values.

    Event Scoping: The client automatically maps WebSocket messages to resource instances. When an event arrives, the client identifies the relevant resource (e.g., a Channel) and emits both a global event and a scoped event specific to that resource's ID, allowing for easier event handling in complex applications.

  8. Use node-ari-client with Promises

    master

    For a more modern asynchronous flow, client.connect returns a Promise. This allows you to chain operations using .then(), .catch(), and .finally(). This pattern is useful for managing complex sequences of ARI operations, such as answering a channel and then playing a specific sound file.

    var client = require('ari-client'),
        Promise = require('bluebird'),
        util = require('util');
    
    client.connect('http://localhost:8088', 'user', 'secret')
      .then(function (ari) {
    
        ari.once('StasisStart', channelJoined);
    
        function channelJoined (event, incoming) {
          incoming.on('ChannelDtmfReceived', dtmfReceived);
    
          incoming.answer()
            .then(function () {
              return play(incoming, 'sound:hello-world');
            })
            .catch(function (err) {});
        }
    
        function dtmfReceived (event, channel) {
          var digit = event.digit;
          switch (digit) {
            case '#':
              play(channel, 'sound:vm-goodbye')
                .then(function () {
                  return channel.hangup();
                })
                .finally(function () {
                  process.exit(0);
                });
              break;
            case '*':
              play(channel, 'sound:tt-monkeys');
              break;
            default:
              play(channel, util.format('sound:digits/%s', digit));
          }
        }
    
        function play (channel, sound) {
          var playback = ari.Playback();
    
          return new Promise(function (resolve, reject) {
            playback.on('PlaybackFinished', function (event, playback) {
              resolve(playback);
            });
    
            channel.play({media: sound}, playback)
              .catch(function (err) {
                reject(err);
              });
          });
        }
    
        ari.start('hello');
      })
      .done(); // program will crash if it fails to connect
  9. Use node-ari-client with Callbacks

    master

    You can use the callback pattern to connect to the Asterisk REST Interface (ARI). The client.connect method accepts the ARI URL, username, and password, and takes a callback function that receives an error and the ari instance. Once connected, you can listen for events like StasisStart and perform operations on channels, such as answering calls or playing media.

    var client = require('ari-client'),
        util = require('util');
    
    client.connect('http://localhost:8088', 'user', 'secret', client_loaded);
    
    function client_loaded (err, ari) {
    
      if (err) {
        throw err; // program will crash if it fails to connect
      }
    
      ari.once('StasisStart', channel_joined);
    
      function channel_joined (event, incoming) {
        incoming.on('ChannelDtmfReceived', dtmf_received);
    
        incoming.answer(function (err) {
          play(incoming, 'sound:hello-world');
        });
      }
    
      function dtmf_received (event, channel) {
        var digit = event.digit;
        switch (digit) {
          case '#':
            play(channel, 'sound:vm-goodbye', function (err) {
              channel.hangup(function (err) {
                process.exit(0);
              });
            });
            break;
          case '*':
            play(channel, 'sound:tt-monkeys');
            break;
          default:
            play(channel, util.format('sound:digits/%s', digit));
        }
      }
    
      function play (channel, sound, callback) {
        var playback = ari.Playback();
    
        playback.on('PlaybackFinished', function (event, playback) {
          if (callback) {
            callback(null);
          }
        });
    
        channel.play({media: sound}, playback, function (err, playback) {});
      }
    
      ari.start('hello');
    }
  10. Manage channel audio (Moh, Silence, Hold, Mute)

    master

    The ari.channels namespace provides methods to control audio playback and channel states:

    • Music on Hold (Moh):
      • startMoh({channelId, mohClass}): Play music on hold to a channel.
      • stopMoh({channelId}): Stop playing music on hold.
    • Silence:
      • startSilence({channelId}): Play silence to a channel.
      • stopSilence({channelId}): Stop playing silence.
    • Hold/Unmute:
      • unhold({channelId}): Remove a channel from hold.
      • unmute({channelId, direction}): Unmute a channel in a specific direction.
    // Example: Start Moh
    ari.channels.startMoh({ channelId: 'val' })
      .then(() => {})
      .catch((err) => {});