npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

node-automatic-api

v0.9.1

Published

A node.js module to interface with the Automatic cloud API

Downloads

13

Readme

node-automatic-api

A node.js module to interface with the Automatic cloud API.

Before Starting

You will need an Automatic account with developer access: OAuth tokens and a Wink account:

  • If you do not already have an Automatic account:

    • Launch iOS app on your smart phone/tablet. (Android folks, fret not, an Android app is on the way!)

    • Follow the directions to create an account.

  • To activate developer access:

    • Go to the Automatic cloud API website and login.

    • Click on 'Sign up for API Access' and follow the directions.

    • You will receive an email when developer access is enabled.

    • At that point, you can go to your Automatic developer dashboard, which will tell you the clientID and clientSecret for your application.

Install

npm install node-automatic-api

API

Load

var AutomaticAPI = require('node-automatic-api');

Login to cloud

The Automatic cloud API requires that a user explicitly authorize your application. This is done by having the application redirect the user's browser to an authorization URL. On success, the browser is sent back to the 'redirect URL' that was registered when you signed up for API access. When authorized, your application will want to store some 'clientState' for the user. The next time the user starts your application, it checks to see if the clientState is available. If so, it can skip the explicit authorization step.

Once your application has clientState, it may perform REST operations (pull), and it may receive webhook calls from the Automatic cloud (push). In order to support push operations, as with the explicity authoriation process, your application will need to run a webserver. Fortuituously, node makes this easy!

However, one of the difficulties is that your application may be running behind a firewall, and that firewall may be NAT'd, and the upstream provider may periodically change your IP address. At the present time, the Automatic cloud API current requires that the redirect URL and webhook URL be defined when you activate developer access. As such, the code fragment below assumes that 'ipaddr' and 'portno.external' will map to your local IP's 'porto.local'.

Authorize (if necessary) and Listen for webhooks

// these are assigned by Automatic when developer access is activated
var clientID     = '...'
  , clientSecret = '...'
  ;

// these are assigned by your ISP, PAAS, etc.
var ipaddr       = '...'
  , portno       = { external: 8894, local: 8894 }
  ;

// if your application has already been authorized, then this should be set accordingly (see below)
var clientState
  ;

var client;

// create a webserver
http.createServer(function(request, response) {
  // GET is used only during authorization, POST is used only by webhooks
  if (request.method !== 'GET') return webhook(request, response);

  request.on('data', function(chunk) {
    // it's a GET, so we can ignore the data..
  }).on('close', function() {
    console.log('http error: premature close');
  }).on('clientError', function(err, socket) {
    console.log('http error: ' + err.message);
  }).on('end', function() {
    var parts, requestURL;

    // look at the URL's query parameters to see if this is from the user or Automatic
    parts = url.parse(request.url, true);

    // it's the user kicking off the authorization process
    if (!parts.query.code) {
      client = new AutomaticAPI.AutomaticAPI({ clientID: clientID, clientSecret: clientSecret }).on('error', function(err) {
        console.log('background error: ' + err.message);
      });

      requestURL = client.authenticateURL(null, 'http://' + ipaddr + ':' + portno.external + '/');
      response.writeHead(307, { location: requestURL, 'content-length' : 0 });
      response.end();
    }

    // it's Automatic confirming the authorization process
    client.authorize(parts.query.code, parts.query.state, function(err, user, state, scopes) {
      if (!!err) return console.log('authorization error: ' + err.message);

      // remember state as clientState for the next time the application runs
      console.log(util.inspect(state, { depth: null }));

      // REST API now available
    });

    // in practice, should give the user a nice "thank you!" screen...
    response.writeHead(200, {'content-length' : 0 });
    return response.end();
  });
}).listen(portno.local, function() {
  // we're now listening, see if we need to authorize
  if (!clientState) return console.log('please connect to http://localhost:' + portno.local + ' to authorize application');

  // already authorized, so we're listening for webhooks, create a client, set the state, and that's it!
  console.log('listening on port ' + portno.local + ' for incoming connections to http://' + ipaddr + ':'
              + portno.external);
  client = new AutomaticAPI.AutomaticAPI({ clientID: clientID , clientSecret: clientSecret }).on('error', function(err) {
    console.log('background error: ' + err.message);
  }).setState(clientState);

  // REST API now available
});

Handling Webhooks

var webhook = function (request, response) {
  var body = '';

  request.setEncoding('utf8');
  request.on('data', function(chunk) {
    body += chunk.toString();
  }).on('close', function() {
    console.log('http error: premature close');
  }).on('clientError', function(err, socket) {/* jshint unused: false */
    console.log('http error: ' + err.message);
  }).on('end', function() {
    var data;

    var loser = function (message) {
      response.writeHead(200, { 'content-type': 'text/plain; charset=utf8', 'content-length' : message.length });
      response.end(message);
    };

    try { data = JSON.parse(body); } catch(ex) { return loser(ex.message); }
    if (!data.type) return loser('webhook missing type parameter');
    if ((!data.user) || (!data.user.id)) return loser('webhook missing user.id');
    client = users[data.user.id];
    if (!client) return loser('internal error (somewhere!)');
    response.writeHead(200, {'content-length' : 0 });
    response.end();

    // now process data.type
    console.log(util.inspect(data, { depth: null }));
  });
};

REST API

client.roundtrip('GET', '/trips', null, function(err, results) {
  if (!!err) return console.log('/trips: ' + err.message);

  console.log('trips');
  console.log(util.inspect(results, { depth: null }));
});

Finally

Enjoy!