OpenVeo Publish server

API Docs for: 8.0.0
Show:

File: app/server/watcher/Watcher.js

'use strict';

/**
 * @module watcher
 */

var path = require('path');
var util = require('util');
var events = require('events');
var async = require('async');
var DirectoryWatcher = process.requirePublish('app/server/watcher/DirectoryWatcher.js');

/**
 * Fired when an error occurred.
 *
 * @event error
 * @param {WatcherError} The error
 */

/**
 * Fired when a new resource (file or directory) has been added to one of the watched folders.
 *
 * @event create
 * @param {String} Path of the added resource
 */

/**
 * Fired when a resource (file or directory) has been deleted from one of the watched folders.
 *
 * @event delete
 * @param {String} Path of the resource before it has been removed
 */

/**
 * Fired when a directory is added to watched directories.
 *
 * Fired after "create" event in case the directory is added to an already watched directory.
 *
 * @event watch
 * @param {String} Path of the directory
 */

/**
 * Defines a watcher to be aware of new resources added to one or several directories.
 *
 * @example
 *     // Create a new watcher
 *     var watcher = new Watcher();
 *
 *     // Listen to watcher events
 *     watcher.on('create', function(resourcePath) {
 *       console.log('A new file has been added : ' + resourcePath);
 *     });
 *
 *     watcher.on('delete', function(resourcePath) {
 *       console.log('A file has been removed : ' + resourcePath);
 *     });
 *
 *     watcher.on('watch', function(directoryPath) {
 *       console.log('A watcher is now running for directory ' + directoryPath);
 *     });
 *
 *     watcher.on('error', function(error) {
 *       console.log(error);
 *     });
 *
 *     // Ask watcher to watch directories
 *     watcher.add([
 *       '/tmp/hotFolder1',
 *       '/tmp/hotFolder2',
 *       '/tmp/hotFolder3'
 *     ], function(results) {
 *       console.log(results);
 *     });
 *
 *     // Ask watcher to stop watching directories
 *     watcher.remove([
 *       '/tmp/hotFolder3'
 *     ]);
 *
 * @class Watcher
 * @constructor
 * @param {Object} [options] Watcher options
 * @param {Number} [options.stabilityThreshold] Number of milliseconds to wait before considering a file
 * as stable
 */
function Watcher(options) {
  Object.defineProperties(this, {

    /**
     * The list of directories' watchers actually running.
     *
     * @property directoriesWatchers
     * @type Array
     * @final
     */
    directoriesWatchers: {
      value: []
    },

    /**
     * Watcher options.
     *
     * @property options
     * @type Object
     * @final
     */
    options: {
      value: options
    }

  });
}

module.exports = Watcher;
util.inherits(Watcher, events.EventEmitter);

/**
 * Checks if a directory is actually being watched.
 *
 * @method isWatched
 * @private
 * @param {String} directoryPath The absolute path of the directory to check
 * @return {Boolean} true if directory is actually being watched, false otherwise
 */
function isWatched(directoryPath) {
  directoryPath = path.resolve(directoryPath);

  for (var i = 0; i < this.directoriesWatchers.length; i++) {
    if (directoryPath === this.directoriesWatchers[i].directoryPath)
      return true;
  }

  return false;
}

/**
 * Adds new directories to watch.
 *
 * @method add
 * @async
 * @param {Array} directoriesPaths The list of absolute directories paths to watch
 * @param {Function} callback The function to call when directories are being watched
 *  - **Array** The list of results with a property "error" if something went wrong
 */
Watcher.prototype.add = function(directoriesPaths, callback) {
  var asyncTasks = [];

  directoriesPaths.forEach((function(directoryPath) {
    asyncTasks.push((function(callback) {
      if (isWatched.call(this, directoryPath))
        return callback(new Error('Directory "' + directoryPath + '" is already being watched'));

      var directoryWatcher = new DirectoryWatcher(directoryPath, this.options);

      directoryWatcher.on('create', (function(resourcePath) {
        this.emit('create', resourcePath);
      }).bind(this));

      directoryWatcher.on('watch', (function(directoryPath) {
        this.emit('watch', directoryPath);
      }).bind(this));

      directoryWatcher.on('delete', (function(resourcePath) {
        this.emit('delete', resourcePath);
      }).bind(this));

      directoryWatcher.on('error', (function(error) {
        this.emit('error', error);
      }).bind(this));

      this.directoriesWatchers.push(directoryWatcher);

      // Watch directory
      directoryWatcher.watch(callback);

    }).bind(this));
  }).bind(this));

  async.parallel(async.reflectAll(asyncTasks), function(error, results) {
    callback(results);
  });
};

/**
 * Stops watching directories.
 *
 * @method remove
 * @param {Array} [directoriesPaths] The list of absolute directories' paths to stop watching. If no directories are
 * specified all watching directories won't be watched anymore
 */
Watcher.prototype.remove = function(directoriesPaths) {
  if (directoriesPaths) {
    directoriesPaths.forEach((function(directoryPath) {
      var index = -1;
      for (var i = 0; i < this.directoriesWatchers.length; i++) {
        var directoryWatcher = this.directoriesWatchers[0];
        if (directoryWatcher.directoryPath === path.resolve(directoryPath)) {
          directoryWatcher.close();
          index = i;
          break;
        }
      }
      if (index > -1)
        this.directoriesWatchers.splice(index, 1);
    }).bind(this));
  } else {
    this.directoriesWatchers.forEach(function(directoryWatcher) {
      directoryWatcher.close();
    });
    this.directoriesWatchers.splice(0, this.directoriesWatchers.length);
  }
};