Source: index.js

/*jslint node:true*/

module.exports = AirWatchService;

var fs = require('fs');
var _ = require('lodash');
var request = require('request');
var winston = require('winston');
var chunkingStreams = require('chunking-streams');
var SizeChunker = chunkingStreams.SizeChunker;
var deviceIdentifiers = ['macaddress', 'serialnumber', 'UDID'];

winston.remove(winston.transports.Console);
winston.add(winston.transports.Console, {'timestamp': true, 'colorize': true});

require('util').inherits(AirWatchService, require('events').EventEmitter);

/**
 * Returns a filesize in bytes of a file.
 * @param {String} filename - Path to file.
 * @returns {Number} bytes - Total file size in bytes.
 */
var getFilesizeInBytes = function getFilesizeInBytes(filename) {
  var stats = fs.statSync(filename);
  var fileSizeInBytes = stats.size;
  return fileSizeInBytes;
};


/**
 * Creates an object of the AirWatchService
 * @param {Object} config - Service configurations object
 * @param {String} config.username - AirWatch API Username.
 * @param {String} config.password - AirWatch API Password.
 * @param {String} config.groupid - Group ID.
 * @param {String} config.apicode - aw-tenant-code.
 * @param {String} config.host - IP Address or domain name of the AirWatch server.
 * @returns {AirWatchService}
 * @class
 * @classdesc This is a description of the AirWatchService class.
 */
var AirWatchService = function AirWatchService(config) {
  if (!(this instanceof AirWatchService)) {
    return new AirWatchService(config);
  }
  require('events').EventEmitter.call(this);

  this.version = '0.0.1';
  this.logger = winston;
  this.config = config; //meh
  this.username = config.username;
  this.password = config.password;
  this.groupid = config.groupid;
  this.apicode = config.apicode;
  this.apiVersion = config.apiVersion || 'v1';
  this.path = 'https://' + config.host + '/api/' + this.apiVersion + '/';
  winston.log('info', 'AirWatchService Loaded');
};

/**
 * Make an request to the AirWatch API
 * @param {String} url - Endpoint to make the request, do not include hostname.
 * @param {Object} post - A json object with data to post. Optional.
 * @param {function} callback - The callback that handles the response.
 * @param {Object} callback.error
 * @param {Object} callback.response
 * @param {Object} callback.body
 */
AirWatchService.prototype.makeRequest = function (url, post, callback) {
  var self, options;
  self = this;
  options = {
    method: post ? 'POST' : 'GET',
    url: self.path + url,
    strictSSL: false,
    followRedirect: true,
    followAllRedirects: true,
    json: true,
    headers: {
      'Host': self.config.host,
      'Accept': 'application/json',
      'User-Agent': 'node-airwatch',
      'aw-tenant-code': self.apicode,
      'Date': new Date().toUTCString(),
      'content-type': 'application/json',
      'Authorization': 'Basic ' + new Buffer(self.username + ':' + self.password).toString('base64')
    }
  };
  if (post) {
    options.body = post;
  }
  request(options, callback);
};

/**
 * Request the current version of an App and update it via semantic versioning. This is currently designed for iOS
 * apps, must have agvtool installed.
 *
 * @param {Object} options
 * @param {String} options.app - Bundle ID of App.
 * @param {String} [options.status=active] - Status of App.
 * @param {String} [options.release=PATCH] - Type of release.
 * @param callback
 */
AirWatchService.prototype.updateVersion = function (options, callback) {
  var self, newVersion, currentVersion, versionSplit, operate, reqUrl;
  self = this;
  if (!options.status) {
    options.status = 'active';
  }
  if (!options.release) {
    options.release = 'PATCH';
  }
  reqUrl = 'mam/apps/search?bundleid=' + options.app + '&status=' + options.status;
  self.makeRequest(reqUrl, null, function (error, request, body) {
    if (request.statusCode !== 200) {
      return callback(error);
    }
    currentVersion = body.Application[0].ActualFileVersion;
    versionSplit = currentVersion.split('.');
    switch (options.release) {
      case ('MAJOR'):
        operate = parseInt(versionSplit[0]);
        operate += 1;
        versionSplit[0] = operate;
        break;
      case ('MINOR'):
        operate = parseInt(versionSplit[1]);
        operate += 1;
        versionSplit[1] = operate;
        break;
      case ('PATCH'):
        operate = parseInt(versionSplit[2]);
        operate += 1;
        versionSplit[2] = operate;
        break;
      default:
        break;
    }
    newVersion = versionSplit.join('.');
    winston.log('info', 'Updating version ' + currentVersion + ' -> ' + newVersion);
    return callback(null);
    //TODO update ios with new version
    //os.system('agvtool new-marketing-version ' + newVer)
  });

};

/**
 * Installs an Application on a device
 * @param {Object} options
 * @param {String} options.TransactionId -
 * @param {function} callback
 */
AirWatchService.prototype.installApp = function (options, callback) {
  var self;
  self = this;
  self.makeRequest('mam/apps/internal/begininstall', options, function (error, response, body) {
    if (error || response.statusCode !== 200) {
      self.emit('debug', 'Install App Failed ' + (error || response.statusCode));
      return callback(error || new Error(response.statusCode));
    }
    if (!body) {
      self.emit('debug', 'Install App Failed');
      return callback(new Error('Invalid Response'));
    }
    callback(null, body);
  });
};

/**
 * Upload an App to the AirWatch API as a single Blob
 * @param {String} filename - Filename of app. Must be in the same directory (for now).
 * @param {function} callback - The callback that handles the response.
 * @param {Object} callback.error
 * @param {Object} callback.response
 * @param {Object} callback.body
 */
AirWatchService.prototype.uploadAppBlob = function (filename, callback) {
  var self, endpoint, options;
  self = this;
  endpoint = 'mam/blobs/uploadblob?filename=' + filename + '&organizationgroupid=' + self.groupid;
  options = {
    method: 'POST',
    url: self.path + endpoint,
    json: true,
    strictSSL: false,
    followRedirect: true,
    followAllRedirects: true,
    headers: {
      'Host': self.config.host,
      'Accept': 'application/json',
      'aw-tenant-code': self.apicode,
      'Date': new Date().toUTCString(),
      'Authorization': 'Basic ' + new Buffer(self.username + ':' + self.password).toString('base64')
    }
  };
  winston.log('info', options.url);
  fs.createReadStream(filename).pipe(request(options, callback));
};

/**
 * Upload Application Chunks (iOS and Android).
 * Uploads application chunks into the AirWatch database for internal application install. This function must be used
 * prior to the 'Begin Install API' for uploading an internal application.
 * @param {String} filename - Name of the file you want to upload.
 * @param {String} callback - Returns a function with the uploaded transactionId.
 */
AirWatchService.prototype.uploadAppChunks = function (filename, callback) {
  var self, chunker, input, totalUploaded, totalAppSize, tmp, endpoint, chunkData, output, transactionId, chunkDataSize;
  self = this;
  tmp = './tmp/';
  endpoint = 'mam/apps/internal/uploadchunk';
  totalAppSize = getFilesizeInBytes(filename);
  totalUploaded = 0;
  input = fs.createReadStream(filename);
  chunker = new SizeChunker({chunkSize: 1024 * 35, flushTail: true});
  chunker.on('chunkStart', function (id, done) {
    chunkData = [];
    chunkDataSize = 0;
    done();
  });
  chunker.on('chunkEnd', function (id, done) {
    var fullChunkToUpload = Buffer.concat(chunkData, chunkDataSize);
    var post = {
      'ChunkData': fullChunkToUpload.toString('base64'),
      'ChunkSequenceNumber': id + 1,
      'TotalApplicationSize': totalAppSize,
      'ChunkSize': fullChunkToUpload.length
    };
    if (transactionId) {
      post.TransactionId = transactionId;
    }
    self.makeRequest(endpoint, post, function (error, response, body) {
      if (!body.UploadSuccess) {
        self.emit('debug', 'problem ' + (error || response.statusCode));
        return callback(error || new Error(response.statusCode));
      }
      transactionId = body.TranscationId;
      totalUploaded += fullChunkToUpload.length;
      if (totalUploaded === totalAppSize) {
        return callback(null, transactionId);
      }
      done();
    });
  });
  chunker.on('data', function (chunk) {
    chunkData.push(chunk.data);
    chunkDataSize += chunk.data.length;
  });
  input.pipe(chunker);
};

/**
 * Returns an object containing an array of applications installed on the device.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param callback
 */
AirWatchService.prototype.getDeviceApps = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, 'apps', callback);
};

/**
 * Retrieves details of the device identified by its numeric ID.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 */
AirWatchService.prototype.getDeviceInfo = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, null, callback);
};

/**
 * Retrieves the details of the certificates that are present on the device.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 */
AirWatchService.prototype.getDeviceCertificates = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, 'certificates', callback);
};

/**
 * Retrieves the details of the compliance policies that are present on a device.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 */
AirWatchService.prototype.getDeviceCompliance = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, 'compliance', callback);
};

/**
 * Retrieves the details of the content that is present on a device.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 */
AirWatchService.prototype.getDeviceContent = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, 'content', callback);
};

/**
 * Retrieves the profile related information of the device.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 */
AirWatchService.prototype.getDeviceProfiles = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, 'profiles', callback);
};

/**
 * Retrieves the event log details of the device.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 */
AirWatchService.prototype.getDeviceEventLog = function (options, callback) {
  var self = this;
  self._makeDeviceRequest(options, 'eventlog', callback);
};

/**
 * Make a request to the API for a device specific action.
 * @param {Object} options - A json object containing items needed to make the request.
 * @param {String} options.deviceId - Type of identification. Can be either 'macaddress', 'serialnumber' or 'UDID'.
 * @param {String} options.uid - Unique ID of the device. Can be either a valid MAC address, serial number or UDID.
 * @param {function} callback
 * @private
 */
AirWatchService.prototype._makeDeviceRequest = function (options, action, callback) {
  var self, endpoint, suffix;
  self = this;
  if (!_.includes(deviceIdentifiers, options.deviceId)) {
    winston.log('warn', 'Bad ID Type');
    //TODO: handle error
  }
  suffix = action ? '/' + action : '';
  endpoint = 'mdm/devices/' + options.deviceId + '/' + options.uid + suffix;
  self.makeRequest(endpoint, null, function (error, response, body) {
    if (error || response.statusCode !== 200) {
      self.emit('debug', 'problem ' + (error || response.statusCode));
      return callback(error || new Error(response.statusCode));
    }
    if (!body) {
      self.emit('debug', 'no body');
      return callback(new Error('Invalid Response'));
    }
    callback(null, body);
  });
};
Node AirWatch - MIT
Copyright © 2015 Edward Knowles
Documentation generated by JSDoc 3.2.2 on Mon Apr 27th 2015 using the DocStrap template.