<commit_before>'use strict';

const { EasyNameable } = require('abstract-things');
const { Vacuum } = require('abstract-things/climate');
const MiioApi = require('../device');

function checkResult(r) {
	if(r !== 0) {
		throw new Error('Could not complete call to device');
	}
}

/**
 * Implementation of the interface used by the Mi Robot Vacuum. This device
 * doesn't use properties via get_prop but instead has a get_status.
 */
module.exports = class extends Vacuum
	.with(MiioApi, EasyNameable)
{
	static get type() {
		return 'miio:vacuum';
	}

	constructor(options) {
		super(options);

		this.type = Vacuum.TYPE;

		this.defineProperty('state', s => {
			switch(s) {
				case 2:
					return 'charger-offline';
				case 5:
					return 'cleaning';
				case 6:
					return 'returning';
				case 8:
					return 'charging';
				case 10:
					return 'paused';
				case 11:
					return 'spot-cleaning';
			}
			return 'unknown-' + s;
		});
		this.defineProperty('error_code', {
			name: 'error',
			mapper: e => {
				switch(e) {
					case 0:
						return null;
					default:
						return {
							code: e,
							message: 'unknown'
						};
				}

				// TODO: Find a list of error codes and map them correctly
			}
		});

		this.defineProperty('battery');
		this.defineProperty('clean_time', {
			name: 'cleanTime',
		});
		this.defineProperty('clean_area', {
			name: 'cleanArea',
			mapper: v => v / 1000000
		});
		this.defineProperty('fan_power', {
			name: 'fanPower'
		});
		this.defineProperty('in_cleaning');

		// Consumable status - times for brushes and filters
		this.defineProperty('main_brush_work_time', {
			name: 'mainBrushWorkTime'
		});
		this.defineProperty('side_brush_work_time', {
			name: 'sideBrushWorkTime'
		});
		this.defineProperty('filterWorkTime', {
			name: 'filterWorkTime'
		});
		this.defineProperty('sensorDirtyTime', {
			name: 'sensorDirtyTime'
		});

		this._monitorInterval = 60000;
	}

	/**
	 * Start a cleaning session.
	 */
	start() {
		return this.call('app_start', [])
			.then(checkResult);
	}

	/**
	 * Pause the current cleaning session.
	 */
	pause() {
		return this.call('app_pause', [])
			.then(checkResult);
	}

	/**
	 * Stop the current cleaning session.
	 */
	stop() {
		return this.call('app_stop', [])
			.then(checkResult);
	}

	/**
	 * Stop the current cleaning session and return to charge.
	 */
	charge() {
		return this.call('app_stop', [])
			.then(checkResult)
			.then(() => this.call('app_charge', []))
			.then(checkResult);
	}

	/**
	 * Start cleaning the current spot.
	 */
	spotClean() {
		return this.call('app_spot', [])
			.then(checkResult);
	}

	/**
	 * Get if the device is charging.
	 */
	get charging() {
		return this.state === 'charging';
	}

	/**
	 * Get if the device is cleaning.
	 */
	get cleaning() {
		switch(this.state) {
			case 'cleaning':
			case 'spot-cleaning':
				return true;
			default:
				return false;
		}
	}

	/**
	 * Get the battery level in percent.
	 */
	get battery() {
		return this.property('battery');
	}

	/**
	 * Get the power of the fan.
	 */
	get fanPower() {
		return this.property('fanPower');
	}

	/**
	 * Set the power of the fan. Usually 38, 60 or 77.
	 */
	setFanPower(power) {
		if(typeof power !== 'number' || power < 0 || power > 100) {
			throw new Error('Power must be a number between 0 and 100');
		}

		return this.call('set_custom_mode', [ power ])
			.then(() => {
				return power;
			});
	}

	/**
	 * Activate the find function, will make the device give off a sound.
	 */
	find() {
		return this.call('find_me', [''])
			.then(() => null);
	}

	/**
	 * Get information about the cleaning history of the device. Contains
	 * information about the number of times it has been started and
	 * the days it has been run.
	 */
	getHistory() {
		return this.call('get_clean_summary')
			.then(result => {
				return {
					count: result[2],
					days: result[3].map(ts => new Date(ts * 1000))
				};
			});
	}

	/**
	 * Get history for the specified day. The day should be fetched from
	 * `getHistory`.
	 */
	getHistoryForDay(day) {
		let record = day;
		if(record instanceof Date) {
			record = Math.floor(record.getTime() / 1000);
		}
		return this.call('get_clean_record', [ record ])
			.then(result => ({
				day: day,
				history: result.map(data => ({
					// Start and end times
					start: new Date(data[0] * 1000),
					end: new Date(data[1] * 1000),

					// How long it took in seconds
					duration: data[2],

					// Area in m2
					area: data[3] / 1000000,

					// If it was a complete run
					complete: data[5] === 1
				}))
			}));
	}

	loadProperties(props) {
		// We override loadProperties to use get_status and get_consumables
		return Promise.all([
			this.call('get_status'),
			this.call('get_consumable')
		]).then(result => {
			const status = result[0][0];
			const consumables = result[1][0];

			const mapped = {};
			props.forEach(prop => {
				let value = status[prop];
				if(typeof value === 'undefined') {
					value = consumables[prop];
				}
				this._pushProperty(mapped, prop, value);
			});
			return mapped;
		});
	}
};
<commit_msg>Rewriting support for vacuum<commit_after>'use strict';

const { EasyNameable, ChargingState, AutonomousCharging } = require('abstract-things');
const {
	Vacuum, CleaningState, AdjustableFanSpeed, AutonomousCleaning, SpotCleaning
} = require('abstract-things/climate');

const MiioApi = require('../device');
const BatteryLevel = require('./capabilities/battery-level');

function checkResult(r) {
	if(r !== 0) {
		throw new Error('Could not complete call to device');
	}
}

/**
 * Implementation of the interface used by the Mi Robot Vacuum. This device
 * doesn't use properties via get_prop but instead has a get_status.
 */
module.exports = class extends Vacuum.with(
	MiioApi, EasyNameable, BatteryLevel, AutonomousCharging, AutonomousCleaning,
	SpotCleaning, AdjustableFanSpeed
) {
	static get type() {
		return 'miio:vacuum';
	}

	constructor(options) {
		super(options);

		this.defineProperty('error_code', {
			name: 'error',
			mapper: e => {
				switch(e) {
					case 0:
						return null;
					default:
						return {
							code: e,
							message: 'Unknown error ' + e
						};
				}

				// TODO: Find a list of error codes and map them correctly
			}
		});

		this.defineProperty('state', s => {
			switch(s) {
				case 1:
					return 'initiating';
				case 2:
					return 'charger-offline';
				case 3:
					return 'waiting';
				case 5:
					return 'cleaning';
				case 6:
					return 'returning';
				case 8:
					return 'charging';
				case 9:
					return 'charging-error';
				case 10:
					return 'paused';
				case 11:
					return 'spot-cleaning';
				case 12:
					return 'error';
				case 13:
					return 'shutting-down';
				case 14:
					return 'updating';
				case 15:
					return 'docking';
				case 100:
					return 'full';
			}
			return 'unknown-' + s;
		});

		// Define the batteryLevel property for monitoring battery
		this.defineProperty('battery', {
			name: 'batteryLevel'
		});

		this.defineProperty('clean_time', {
			name: 'cleanTime',
		});
		this.defineProperty('clean_area', {
			name: 'cleanArea',
			mapper: v => v / 1000000
		});
		this.defineProperty('fan_power', {
			name: 'fanSpeed'
		});
		this.defineProperty('in_cleaning');

		// Consumable status - times for brushes and filters
		this.defineProperty('main_brush_work_time', {
			name: 'mainBrushWorkTime'
		});
		this.defineProperty('side_brush_work_time', {
			name: 'sideBrushWorkTime'
		});
		this.defineProperty('filterWorkTime', {
			name: 'filterWorkTime'
		});
		this.defineProperty('sensorDirtyTime', {
			name: 'sensorDirtyTime'
		});

		this._monitorInterval = 60000;
	}

	propertyUpdated(key, value, oldValue) {
		if(key === 'state') {
			// Update charging state
			this.updateCharging(value === 'charging');

			switch(value) {
				case 'cleaning':
				case 'spot-cleaning':
					// The vacuum is cleaning
					this.updateCleaning(true);
					break;
				case 'paused':
					// Cleaning has been paused, do nothing special
					break;
				case 'error':
					// An error has occurred, rely on error mapping
					this.updateError(this.property('error'));
					break;
				case 'charging-error':
					// Charging error, trigger an error
					this.updateError({
						code: 'charging-error',
						message: 'Error during charging'
					});
					break;
				case 'charger-offline':
					// Charger is offline, trigger an error
					this.updateError({
						code: 'charger-offline',
						message: 'Charger is offline'
					});
					break;
				default:
					// The vacuum is not cleaning
					this.updateCleaning(false);
					break;
			}
		} else if(key === 'fanSpeed') {
			this.updateFanSpeed(value);
		}

		super.propertyUpdated(key, value, oldValue);
	}

	/**
	 * Start a cleaning session.
	 */
	activateCleaning() {
		return this.call('app_start', [])
			.then(checkResult);
	}

	/**
	 * Pause the current cleaning session.
	 */
	pause() {
		return this.call('app_pause', [])
			.then(checkResult);
	}

	/**
	 * Stop the current cleaning session.
	 */
	deactivateCleaning() {
		return this.call('app_stop', [])
			.then(checkResult);
	}

	/**
	 * Stop the current cleaning session and return to charge.
	 */
	activateCharging() {
		return this.call('app_stop', [])
			.then(checkResult)
			.then(() => this.call('app_charge', []))
			.then(checkResult);
	}

	/**
	 * Start cleaning the current spot.
	 */
	activateSpotClean() {
		return this.call('app_spot', [])
			.then(checkResult);
	}

	/**
	 * Set the power of the fan. Usually 38, 60 or 77.
	 */
	changeFanSpeed(speed) {
		return this.call('set_custom_mode', [ speed ], {
			refresh: true
		});
	}

	/**
	 * Activate the find function, will make the device give off a sound.
	 */
	find() {
		return this.call('find_me', [''])
			.then(() => null);
	}

	/**
	 * Get information about the cleaning history of the device. Contains
	 * information about the number of times it has been started and
	 * the days it has been run.
	 */
	getHistory() {
		return this.call('get_clean_summary')
			.then(result => {
				return {
					count: result[2],
					days: result[3].map(ts => new Date(ts * 1000))
				};
			});
	}

	/**
	 * Get history for the specified day. The day should be fetched from
	 * `getHistory`.
	 */
	getHistoryForDay(day) {
		let record = day;
		if(record instanceof Date) {
			record = Math.floor(record.getTime() / 1000);
		}
		return this.call('get_clean_record', [ record ])
			.then(result => ({
				day: day,
				history: result.map(data => ({
					// Start and end times
					start: new Date(data[0] * 1000),
					end: new Date(data[1] * 1000),

					// How long it took in seconds
					duration: data[2],

					// Area in m2
					area: data[3] / 1000000,

					// If it was a complete run
					complete: data[5] === 1
				}))
			}));
	}

	loadProperties(props) {
		// We override loadProperties to use get_status and get_consumables
		return Promise.all([
			this.call('get_status'),
			this.call('get_consumable')
		]).then(result => {
			const status = result[0][0];
			const consumables = result[1][0];

			const mapped = {};
			props.forEach(prop => {
				let value = status[prop];
				if(typeof value === 'undefined') {
					value = consumables[prop];
				}
				this._pushProperty(mapped, prop, value);
			});
			return mapped;
		});
	}
};
<|endoftext|>


<commit_before>/**
 * @param {Object|Function} modelOrFetchFn - Mongoose model or fetch function that returns a promise
 * @param {Object}          [options]
 * @param {Function}          [options.errorFactory]
 * @param {String}            [options.errorMessage]
 * @param {String}            [options.fieldName=_id] - Only for model
 * @param {String}            [options.objectName] - Required for fetchFn
 * @param {Boolean}           [options.passErrorToNext=true]
 *
 * @returns {Function}
 */
function loadObject(modelOrFetchFn, options) {
    options = Object.assign({}, loadObject.options, options);

    var errorMessage = options.errorMessage;
    var errorFactory = options.errorFactory || function(message) {
        var err = new Error(message);
        err.status = 404;
        return err;
    };

    var objectName      = options.objectName;
    var passErrorToNext = options.hasOwnProperty('passErrorToNext') ? options.passErrorToNext : true;

    var fetchFn;

    if (modelOrFetchFn.findOne instanceof Function) {
        var model     = modelOrFetchFn;
        var fieldName = options.fieldName || '_id';

        if (!objectName) {
            objectName = model.modelName[0].toLowerCase() + model.modelName.slice(1);
        }

        fetchFn = function(req, value) {
            return model.findOne({ [fieldName]: value });
        };
    } else {
        fetchFn = modelOrFetchFn;
        if (!objectName) {
            throw new Error('Object name for fetchFn must be set');
        }
    }

    return function(req, res, next, value, name) {
        fetchFn(req, req.params[name])
            .then(function(object) {
                if (!object && passErrorToNext) {
                    var err = errorFactory(errorMessage);
                    return Promise.reject(err);
                }

                req[objectName] = object;

                next();
            })
            .catch(next);
    };
}

module.exports = loadObject;<commit_msg>fetch -> load, fn -> function<commit_after>/**
 * @param {Object|Function} modelOrLoadFunction - Mongoose model or load function that returns a promise
 * @param {Object}          [options]
 * @param {Function}          [options.errorFactory]
 * @param {String}            [options.errorMessage]
 * @param {String}            [options.fieldName=_id] - Only for model
 * @param {String}            [options.objectName] - Required for load function
 * @param {Boolean}           [options.passErrorToNext=true]
 *
 * @returns {Function}
 */
function loadObject(modelOrLoadFunction, options) {
    options = Object.assign({}, loadObject.options, options);

    var errorMessage = options.errorMessage;
    var errorFactory = options.errorFactory || function(message) {
        var err = new Error(message);
        err.status = 404;
        return err;
    };

    var objectName      = options.objectName;
    var passErrorToNext = options.hasOwnProperty('passErrorToNext') ? options.passErrorToNext : true;

    var loadFunction;

    if (modelOrLoadFunction.findOne instanceof Function) {
        var model     = modelOrLoadFunction;
        var fieldName = options.fieldName || '_id';

        if (!objectName) {
            objectName = model.modelName[0].toLowerCase() + model.modelName.slice(1);
        }

        loadFunction = function(req, value) {
            return model.findOne({ [fieldName]: value });
        };
    } else {
        loadFunction = modelOrLoadFunction;
        if (!objectName) {
            throw new Error('objectName for load function must be set');
        }
    }

    return function(req, res, next, value, name) {
        loadFunction(req, req.params[name])
            .then(function(object) {
                if (!object && passErrorToNext) {
                    var err = errorFactory(errorMessage);
                    return Promise.reject(err);
                }

                req[objectName] = object;

                next();
            })
            .catch(next);
    };
}

module.exports = loadObject;<|endoftext|>


<commit_before>'use strict'

const binding = require('bindings')('v8is')

exports.isUndefined = function isUndefined(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUndefined(obj)
}

exports.isNull = function isNull(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isNull(obj)
}

exports.isTrue = function isTrue(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isTrue(obj)
}

exports.isFalse = function isFalse(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFalse(obj)
}

exports.isString = function isString(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isString(obj)
}

exports.isSymbol = function isSymbol(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSymbol(obj)
}

exports.isFunction = function isFunction(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFunction(obj)
}

exports.isArray = function isArray(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArray(obj)
}

exports.isObject = function isObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isObject(obj)
}

exports.isBoolean = function isBoolean(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isBoolean(obj)
}

exports.isNumber = function isNumber(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isNumber(obj)
}

exports.isInt32 = function isInt32(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt32(obj)
}

exports.isUint32 = function isUint32(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint32(obj)
}

exports.isDate = function isDate(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isDate(obj)
}

exports.isArgumentsObject = function isArgumentsObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArgumentsObject(obj)
}

exports.isBooleanObject = function isBooleanObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isBooleanObject(obj)
}

exports.isNumberObject = function isNumberObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isNumberObject(obj)
}

exports.isStringObject = function isStringObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isStringObject(obj)
}

exports.isRegExp = function isRegExp(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isRegExp(obj)
}

exports.isGeneratorFunction = function isGeneratorFunction(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isGeneratorFunction(obj)
}

exports.isGeneratorObject = function isGeneratorObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isGeneratorObject(obj)
}

exports.isPromise = function isPromise(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isPromise(obj)
}

exports.isMap = function isMap(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isMap(obj)
}

exports.isSet = function isSet(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSet(obj)
}

exports.isMapIterator = function isMapIterator(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isMapIterator(obj)
}

exports.isSetIterator = function isSetIterator(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSetIterator(obj)
}

exports.isWeakMap = function isWeakMap(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isWeakMap(obj)
}

exports.isWeakSet = function isWeakSet(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isWeakSet(obj)
}

exports.isArrayBuffer = function isArrayBuffer(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArrayBuffer(obj)
}

exports.isArrayBufferView = function isArrayBufferView(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArrayBufferView(obj)
}

exports.isTypedArray = function isTypedArray(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isTypedArray(obj)
}

exports.isUint8Array = function isUint8Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint8Array(obj)
}

exports.isInt8Array = function isInt8Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt8Array(obj)
}

exports.isUint16Array = function isUint16Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint16Array(obj)
}

exports.isInt16Array = function isInt16Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt16Array(obj)
}

exports.isInt32Array = function isInt32Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt32Array(obj)
}

exports.isUint32Array = function isUint32Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint32Array(obj)
}

exports.isFloat32Array = function isFloat32Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFloat32Array(obj)
}

exports.isFloat64Array = function isFloat64Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFloat64Array(obj)
}

exports.isDateView = function isDataView(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isDataView(obj)
}

exports.isSharedArrayBuffer = function isSharedArrayBuffer(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSharedArrayBuffer(obj)
}
<commit_msg>index: remove js side of isSharedArrayBuffer<commit_after>'use strict'

const binding = require('bindings')('v8is')

exports.isUndefined = function isUndefined(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUndefined(obj)
}

exports.isNull = function isNull(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isNull(obj)
}

exports.isTrue = function isTrue(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isTrue(obj)
}

exports.isFalse = function isFalse(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFalse(obj)
}

exports.isString = function isString(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isString(obj)
}

exports.isSymbol = function isSymbol(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSymbol(obj)
}

exports.isFunction = function isFunction(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFunction(obj)
}

exports.isArray = function isArray(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArray(obj)
}

exports.isObject = function isObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isObject(obj)
}

exports.isBoolean = function isBoolean(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isBoolean(obj)
}

exports.isNumber = function isNumber(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isNumber(obj)
}

exports.isInt32 = function isInt32(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt32(obj)
}

exports.isUint32 = function isUint32(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint32(obj)
}

exports.isDate = function isDate(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isDate(obj)
}

exports.isArgumentsObject = function isArgumentsObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArgumentsObject(obj)
}

exports.isBooleanObject = function isBooleanObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isBooleanObject(obj)
}

exports.isNumberObject = function isNumberObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isNumberObject(obj)
}

exports.isStringObject = function isStringObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isStringObject(obj)
}

exports.isRegExp = function isRegExp(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isRegExp(obj)
}

exports.isGeneratorFunction = function isGeneratorFunction(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isGeneratorFunction(obj)
}

exports.isGeneratorObject = function isGeneratorObject(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isGeneratorObject(obj)
}

exports.isPromise = function isPromise(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isPromise(obj)
}

exports.isMap = function isMap(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isMap(obj)
}

exports.isSet = function isSet(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSet(obj)
}

exports.isMapIterator = function isMapIterator(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isMapIterator(obj)
}

exports.isSetIterator = function isSetIterator(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isSetIterator(obj)
}

exports.isWeakMap = function isWeakMap(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isWeakMap(obj)
}

exports.isWeakSet = function isWeakSet(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isWeakSet(obj)
}

exports.isArrayBuffer = function isArrayBuffer(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArrayBuffer(obj)
}

exports.isArrayBufferView = function isArrayBufferView(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isArrayBufferView(obj)
}

exports.isTypedArray = function isTypedArray(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isTypedArray(obj)
}

exports.isUint8Array = function isUint8Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint8Array(obj)
}

exports.isInt8Array = function isInt8Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt8Array(obj)
}

exports.isUint16Array = function isUint16Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint16Array(obj)
}

exports.isInt16Array = function isInt16Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt16Array(obj)
}

exports.isInt32Array = function isInt32Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isInt32Array(obj)
}

exports.isUint32Array = function isUint32Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isUint32Array(obj)
}

exports.isFloat32Array = function isFloat32Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFloat32Array(obj)
}

exports.isFloat64Array = function isFloat64Array(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isFloat64Array(obj)
}

exports.isDateView = function isDataView(obj) {
  if (arguments.length < 1)
    throw new Error('argument is required')

  return binding.isDataView(obj)
}
<|endoftext|>


<commit_before>var EventEmitter = require('events').EventEmitter
var duplex       = require('duplex')

var LiveStream   = require('level-live-stream')
var REDIS        = require('redis-protocol-stream')

var makeSchema   = require('./lib/schema')
var BufferedOpener
                 = require('./lib/buffered-opener')
var MakeCreateStream
                 = require('./lib/stream')
var ranges       = require('map-reduce/range')

//need a seperator that sorts early.
//use NULL instead?

module.exports = function (db, id, schema) {

  //none of these should be used.
  var sep = '\x00'
  var localDb     = db.sublevel('sb')
  var replicateDb = db.sublevel('replicate')
  var vectorDb    = db.sublevel('vector')
  var sources = {}

  if('string' !== typeof id)
    schema = id, id = null

  id = id || uuid()

  var match = makeSchema(schema, id)

  //create a new scuttlebutt attachment.
  //a document that is modeled as a range of keys,
  //rather than as a single {key: value} pair

  //WHY DID I DO THIS? - remove this and it works.
  //but it seems to be problem with r-array...
  function checkOld (id, ts) {
    return false
    if(sources[id] && sources[id] >= ts) return true
    sources[id] = ts
  }

  var _batch = [], queued = false

  function save() {
    if(!queued)
      process.nextTick(function () {
        db.batch(_batch, function () {
          db.emit('drain')
        })
        queued = false
        _batch = []
      })
    queued = true
  }

  db.scuttlebutt = function () {
    var args = [].slice.call(arguments)
    return db.scuttlebutt.open.apply(null, args)
  }

  function key() {
    return [].slice.call(arguments).join(sep)
  }

  function insertBatch (_id, doc_id, ts, value, emitter) {
    ts = ts.toString()

    _batch.push({
      key: key(doc_id, ts, _id),
      value: value, type: 'put',
      prefix: localDb
    })

    _batch.push({
      //the second time, so that documents can be rapidly replicated.
      key: key(_id, ts, doc_id),
      value: value, type: 'put',
      prefix: replicateDb
    })

    _batch.push({
      //also, update the vector clock for this replication range,
      //so that it's easy to recall what are the latest documents are.
      //this vector clock is for all the documents, not just this one...
      key: _id, value: ''+ts, type: 'put',
      prefix: vectorDb
    })

    //or do this async?
    //YES, do this async, providing a way to wait until the given
    //maps have been flushed.

    if(emitter && emitter.toJSON)
      _batch.push({
        key: doc_id,
        value: JSON.stringify(emitter.toJSON()),
        type: 'put',
        prefix: db
      })

    save()
  }

  function deleteBatch (_id, doc_id, ts) {

    _batch.push({
      key: key(doc_id, ts, _id),
      type: 'del', prefix: localDb
    })

    _batch.push({
      key: key(_id, ts, doc_id),
      type: 'del', prefix: replicateDb
    })

    save()
  }

  var dbO = new EventEmitter()
  dbO.open = function (key, tail, callback) {
    if('function' === typeof tail) callback = tail, tail = true

    if(!key) throw new Error('must provide a doc_id')
    var scuttlebutt
    if('string' === typeof key) {
      scuttlebutt = match(key)
      scuttlebutt.name = key

      'function' === scuttlebutt.setId
         ? scuttlebutt.setId(id)
         : scuttlebutt.id = id

    } else {
      scuttlebutt = key
      key = scuttlebutt.name
    }

    //*************************************
    //*TODO: replace this with pull-level *
    //*there is a bug in liveStream.      *
    //*(order is not gaurenteed if there  *
    //*   are live updates)               *
    //*************************************
    var stream = LiveStream(localDb, {
          start: [key,   0].join(sep),
          end  : [key, '~'].join(sep)
        })
        .on('data', function (data) {
          //ignore deletes,
          //deletes must be an update.
          if(data.type == 'del') return

          var ary    = data.key.split(sep)
          var ts     = Number(ary[1])
          var source = ary[2]
          var change = JSON.parse(data.value)

          scuttlebutt._update([change, ts, source])
        })

    //this scuttlebutt instance is up to date with the db.

    var ready = false
    function onReady () {
      if(ready) return
      ready = true
      scuttlebutt.emit('sync')
      if(callback) callback(null, scuttlebutt)
    }

    stream.once('sync', onReady)
    stream.once('end' , onReady)

    scuttlebutt.once('dispose', function () {
      //levelup/read-stream throws if the stream has already ended
      //but it's just a user error, not a serious problem.
      try { stream.destroy() } catch (_) { }
    })

    //write the update twice,
    //the first time, to store the document.
    //maybe change scuttlebutt so that value is always a string?
    //If i write a bunch of batches, will they come out in order?
    //because I think updates are expected in order, or it will break.

    function onUpdate (update) {
      var value = update[0], ts = update[1], id = update[2]
      insertBatch (id, key, ts, JSON.stringify(value), scuttlebutt)
    }

    //If the user has passed in a scuttlebutt instead of a key,
    //then it may have history, so save that.
    scuttlebutt.history().forEach(onUpdate)

    //then, track real-time updates
    scuttlebutt.on('_update', onUpdate)

    //an update is now no longer significant
    //can clean it from the database,
    //this isn't to save space so much as it is to
    //save time on future reads.
    scuttlebutt.on('_remove', function (update) {
      var ts = update[1], id = update[2]
      deleteBatch (id, key, ts)
    })

    return scuttlebutt
  }

  dbO.createStream = function () {
    var mx = MuxDemux(function (stream) {
      if(!db) return stream.error('cannot access database this end')

      if('string' === typeof stream.meta) {
        var ts = through().pause()
        //TODO. make mux-demux pause.

        stream.pipe(ts)
        //load the scuttlebutt with the callback,
        //and then connect the stream to the client
        //so that the 'sync' event fires the right time,
        //and the open method works on the client too.
        opener.open(stream.meta, function (err, doc) {
          ts.pipe(doc.createStream()).pipe(stream)
          ts.resume()
        })
      } else if(Array.isArray(stream.meta)) {
        //reduce the 10 most recently modified documents.
        opener.view.apply(null, stream.meta)
          .pipe(through(function (data) {
            this.queue({
              key: data.key.toString(),
              value: data.value.toString()
            })
          }))
          .pipe(stream)
      }
    })
    //clean up
    function onClose () { mx.end() }

    db.once('close', onClose)
    mx.once('close', function () { db.removeListener('close', onClose) })

    return mx
  }

  //THIS IS JUST LEGACY STUFF NOW,
  //TODO: rewrite the streaming/client api,
  //so this disappears.

  db.views = {}
  dbO.view = function (name, opts) {
    if(!opts)
      opts = name, name = opts.name
    if(opts.range) {
      var r      = ranges.range(opts.range)
      opts.start = r.start
      opts.end   = r.end
    }
    if(db.views[name])
      return LiveStream(db.views[name], opts)
    throw new Error('no view named:', name)
  }

  db.on('close', function () {
    opener.emit('close')
  })

  var opener = BufferedOpener(schema, id).swap(dbO)

  db.open =
  db.scuttlebutt.open = opener.open
  db.scuttlebutt._opener = dbO
  db.scuttlebutt.view = opener.view
  db.createRemoteStream =
  db.scuttlebutt.createRemoteStream = MakeCreateStream(opener)

  db.createReplicateStream =
  db.scuttlebutt.createReplicateStream = function (opts) {
    opts = opts || {}
    var yourClock, myClock
    var d = duplex ()
    var outer = REDIS.serialize(d)
    d.on('_data', function (data) {
      if(data.length === 1) {
        //like a telephone, say
        if(''+data[0] === 'BYE') {
          d._end()
        } else {
          //data should be {id: ts}
          yourClock = JSON.parse(data.shift())
          start()
        }
      } else {
        //maybe increment the clock for this node,
        //so that when we detect that a record has been written,
        //can avoid updating the model twice when recieving
        var id = ''+data[0]
        var ts = Number(''+data[1])

        if(!myClock || !myClock[id] || myClock[id] < ts) {
          var doc_id = data[2]
          var value = data[3]

          insertBatch(id, doc_id, ts, value)
          myClock[id] = ts
        }
      }
    })

    function start() {
      if(!(myClock && yourClock)) return

      var clock = {}
      for(var id in myClock)
        clock[id] = ''

      for(var id in yourClock)
        clock[id] = yourClock[id]

      var started = 0
      for(var id in clock) {
        (function (id) {
          started ++
          var _opts = {
            start: [id, clock[id]].join(sep),
            end  : [id, '\xff'   ].join(sep),
            tail : opts.tail
          }
          //TODO, merge stream that efficiently handles back pressure
          //when reading from many streams.
          var stream = LiveStream(replicateDb, _opts)
            .on('data', function (data) {
              var ary = data.key.split(sep)
              ary.push(data.value)
              d._data(ary)
            })
            .once('end', function () {
              if(--started) return
              if(opts.tail === false) d._data(['BYE'])
            })

          d.on('close', stream.destroy.bind(stream))

        })(id);
      }
    }
    db.scuttlebutt.vectorClock(function (err, clock) {
      myClock = clock
      d._data([JSON.stringify(clock)])
      start()
    })

    return outer
  }

  //read the vector clock. {id: ts, ...} pairs.

  db.vectorClock =
  db.scuttlebutt.vectorClock = function (cb) {
    var clock = {}
    vectorDb.createReadStream()
      .on('data', function (data) {
        clock[data.key] = Number(''+data.value)
      })
      .on('close', function () {
        cb(null, clock)
      })
  }
}

<commit_msg>remove comment about fixed issue<commit_after>var EventEmitter = require('events').EventEmitter
var duplex       = require('duplex')

var LiveStream   = require('level-live-stream')
var REDIS        = require('redis-protocol-stream')

var makeSchema   = require('./lib/schema')
var BufferedOpener
                 = require('./lib/buffered-opener')
var MakeCreateStream
                 = require('./lib/stream')
var ranges       = require('map-reduce/range')

//need a seperator that sorts early.
//use NULL instead?

module.exports = function (db, id, schema) {

  //none of these should be used.
  var sep = '\x00'
  var localDb     = db.sublevel('sb')
  var replicateDb = db.sublevel('replicate')
  var vectorDb    = db.sublevel('vector')
  var sources = {}

  if('string' !== typeof id)
    schema = id, id = null

  id = id || uuid()

  var match = makeSchema(schema, id)

  //create a new scuttlebutt attachment.
  //a document that is modeled as a range of keys,
  //rather than as a single {key: value} pair

  //WHY DID I DO THIS? - remove this and it works.
  //but it seems to be problem with r-array...
  function checkOld (id, ts) {
    return false
    if(sources[id] && sources[id] >= ts) return true
    sources[id] = ts
  }

  var _batch = [], queued = false

  function save() {
    if(!queued)
      process.nextTick(function () {
        db.batch(_batch, function () {
          db.emit('drain')
        })
        queued = false
        _batch = []
      })
    queued = true
  }

  db.scuttlebutt = function () {
    var args = [].slice.call(arguments)
    return db.scuttlebutt.open.apply(null, args)
  }

  function key() {
    return [].slice.call(arguments).join(sep)
  }

  function insertBatch (_id, doc_id, ts, value, emitter) {
    ts = ts.toString()

    _batch.push({
      key: key(doc_id, ts, _id),
      value: value, type: 'put',
      prefix: localDb
    })

    _batch.push({
      //the second time, so that documents can be rapidly replicated.
      key: key(_id, ts, doc_id),
      value: value, type: 'put',
      prefix: replicateDb
    })

    _batch.push({
      //also, update the vector clock for this replication range,
      //so that it's easy to recall what are the latest documents are.
      //this vector clock is for all the documents, not just this one...
      key: _id, value: ''+ts, type: 'put',
      prefix: vectorDb
    })

    //or do this async?
    //YES, do this async, providing a way to wait until the given
    //maps have been flushed.

    if(emitter && emitter.toJSON)
      _batch.push({
        key: doc_id,
        value: JSON.stringify(emitter.toJSON()),
        type: 'put',
        prefix: db
      })

    save()
  }

  function deleteBatch (_id, doc_id, ts) {

    _batch.push({
      key: key(doc_id, ts, _id),
      type: 'del', prefix: localDb
    })

    _batch.push({
      key: key(_id, ts, doc_id),
      type: 'del', prefix: replicateDb
    })

    save()
  }

  var dbO = new EventEmitter()
  dbO.open = function (key, tail, callback) {
    if('function' === typeof tail) callback = tail, tail = true

    if(!key) throw new Error('must provide a doc_id')
    var scuttlebutt
    if('string' === typeof key) {
      scuttlebutt = match(key)
      scuttlebutt.name = key

      'function' === scuttlebutt.setId
         ? scuttlebutt.setId(id)
         : scuttlebutt.id = id

    } else {
      scuttlebutt = key
      key = scuttlebutt.name
    }

    var stream = LiveStream(localDb, {
          start: [key,   0].join(sep),
          end  : [key, '~'].join(sep)
        })
        .on('data', function (data) {
          //ignore deletes,
          //deletes must be an update.
          if(data.type == 'del') return

          var ary    = data.key.split(sep)
          var ts     = Number(ary[1])
          var source = ary[2]
          var change = JSON.parse(data.value)

          scuttlebutt._update([change, ts, source])
        })

    //this scuttlebutt instance is up to date with the db.

    var ready = false
    function onReady () {
      if(ready) return
      ready = true
      scuttlebutt.emit('sync')
      if(callback) callback(null, scuttlebutt)
    }

    stream.once('sync', onReady)
    stream.once('end' , onReady)

    scuttlebutt.once('dispose', function () {
      //levelup/read-stream throws if the stream has already ended
      //but it's just a user error, not a serious problem.
      try { stream.destroy() } catch (_) { }
    })

    //write the update twice,
    //the first time, to store the document.
    //maybe change scuttlebutt so that value is always a string?
    //If i write a bunch of batches, will they come out in order?
    //because I think updates are expected in order, or it will break.

    function onUpdate (update) {
      var value = update[0], ts = update[1], id = update[2]
      insertBatch (id, key, ts, JSON.stringify(value), scuttlebutt)
    }

    //If the user has passed in a scuttlebutt instead of a key,
    //then it may have history, so save that.
    scuttlebutt.history().forEach(onUpdate)

    //then, track real-time updates
    scuttlebutt.on('_update', onUpdate)

    //an update is now no longer significant
    //can clean it from the database,
    //this isn't to save space so much as it is to
    //save time on future reads.
    scuttlebutt.on('_remove', function (update) {
      var ts = update[1], id = update[2]
      deleteBatch (id, key, ts)
    })

    return scuttlebutt
  }

  dbO.createStream = function () {
    var mx = MuxDemux(function (stream) {
      if(!db) return stream.error('cannot access database this end')

      if('string' === typeof stream.meta) {
        var ts = through().pause()
        //TODO. make mux-demux pause.

        stream.pipe(ts)
        //load the scuttlebutt with the callback,
        //and then connect the stream to the client
        //so that the 'sync' event fires the right time,
        //and the open method works on the client too.
        opener.open(stream.meta, function (err, doc) {
          ts.pipe(doc.createStream()).pipe(stream)
          ts.resume()
        })
      } else if(Array.isArray(stream.meta)) {
        //reduce the 10 most recently modified documents.
        opener.view.apply(null, stream.meta)
          .pipe(through(function (data) {
            this.queue({
              key: data.key.toString(),
              value: data.value.toString()
            })
          }))
          .pipe(stream)
      }
    })
    //clean up
    function onClose () { mx.end() }

    db.once('close', onClose)
    mx.once('close', function () { db.removeListener('close', onClose) })

    return mx
  }

  //THIS IS JUST LEGACY STUFF NOW,
  //TODO: rewrite the streaming/client api,
  //so this disappears.

  db.views = {}
  dbO.view = function (name, opts) {
    if(!opts)
      opts = name, name = opts.name
    if(opts.range) {
      var r      = ranges.range(opts.range)
      opts.start = r.start
      opts.end   = r.end
    }
    if(db.views[name])
      return LiveStream(db.views[name], opts)
    throw new Error('no view named:', name)
  }

  db.on('close', function () {
    opener.emit('close')
  })

  var opener = BufferedOpener(schema, id).swap(dbO)

  db.open =
  db.scuttlebutt.open = opener.open
  db.scuttlebutt._opener = dbO
  db.scuttlebutt.view = opener.view
  db.createRemoteStream =
  db.scuttlebutt.createRemoteStream = MakeCreateStream(opener)

  db.createReplicateStream =
  db.scuttlebutt.createReplicateStream = function (opts) {
    opts = opts || {}
    var yourClock, myClock
    var d = duplex ()
    var outer = REDIS.serialize(d)
    d.on('_data', function (data) {
      if(data.length === 1) {
        //like a telephone, say
        if(''+data[0] === 'BYE') {
          d._end()
        } else {
          //data should be {id: ts}
          yourClock = JSON.parse(data.shift())
          start()
        }
      } else {
        //maybe increment the clock for this node,
        //so that when we detect that a record has been written,
        //can avoid updating the model twice when recieving
        var id = ''+data[0]
        var ts = Number(''+data[1])

        if(!myClock || !myClock[id] || myClock[id] < ts) {
          var doc_id = data[2]
          var value = data[3]

          insertBatch(id, doc_id, ts, value)
          myClock[id] = ts
        }
      }
    })

    function start() {
      if(!(myClock && yourClock)) return

      var clock = {}
      for(var id in myClock)
        clock[id] = ''

      for(var id in yourClock)
        clock[id] = yourClock[id]

      var started = 0
      for(var id in clock) {
        (function (id) {
          started ++
          var _opts = {
            start: [id, clock[id]].join(sep),
            end  : [id, '\xff'   ].join(sep),
            tail : opts.tail
          }
          //TODO, merge stream that efficiently handles back pressure
          //when reading from many streams.
          var stream = LiveStream(replicateDb, _opts)
            .on('data', function (data) {
              var ary = data.key.split(sep)
              ary.push(data.value)
              d._data(ary)
            })
            .once('end', function () {
              if(--started) return
              if(opts.tail === false) d._data(['BYE'])
            })

          d.on('close', stream.destroy.bind(stream))

        })(id);
      }
    }
    db.scuttlebutt.vectorClock(function (err, clock) {
      myClock = clock
      d._data([JSON.stringify(clock)])
      start()
    })

    return outer
  }

  //read the vector clock. {id: ts, ...} pairs.

  db.vectorClock =
  db.scuttlebutt.vectorClock = function (cb) {
    var clock = {}
    vectorDb.createReadStream()
      .on('data', function (data) {
        clock[data.key] = Number(''+data.value)
      })
      .on('close', function () {
        cb(null, clock)
      })
  }
}

<|endoftext|>


<commit_before>var youtubedl = require('youtube-dl');
var schedule = require('node-schedule');
var config = require('./config.json');

var videoURL = config.videoURL;

var getStream = youtubedl.exec(videoURL, ['-x', '--audio-format', 'mp3'], {}, function(err, output) {
      if (err) throw err;
        console.log(output.join('\n'));
});

var rule = new schedule.RecurrenceRule();
rule.dayOfWeek = new schedule.Range(1, 5);
rule.hour = 4;
rule.minute = 30;

var j = schedule.scheduleJob(rule, getStream);

<commit_msg>Potentially working scheduled download<commit_after>var youtubedl = require('youtube-dl');
var schedule = require('node-schedule');
var config = require('./config.json');

var videoURL = config.videoURL;

function getStream() {
    youtubedl.exec(videoURL, [], {},
        function(err, output) {
            if (err) {
                console.log("Error thrown");
                console.log(err);
                return;
            }
            console.log(output.join('\n'));
        }
    );
}

// M-F, 4:00 - 5:59
var rule = new schedule.RecurrenceRule();
rule.dayOfWeek = new schedule.Range(1, 5);
rule.hour = new schedule.Range(4, 5);
rule.minute = new schedule.Range(0, 59);
rule.second = 0;

var j = schedule.scheduleJob(rule, function () {
    console.log('Attempting download...');
    getStream();
});

<|endoftext|>


<commit_before>'use strict';

var KEEP_ALIVE = 30 * 1000;
var Duplex     = require('stream').Duplex;
var inherits   = require('util').inherits;
var logfmt     = require('logfmt');

/**
 * A Stream for managing communication between a ShareJS client and a ws client
 *
 *     var ShareJSStream = require('share-js-stream');
 *     var WsServer      = require('ws').Server;
 *     var http          = require('http');
 *     var livedb        = require('livedb');
 *     var shareServer   = require('share').server.createClient({
 *       backend: livedb.client(livedb.memory())
 *     });
 *
 *     http.createServer().listen(process.env.PORT, function onListen() {
 *       var wsServer = new WsServer({ server: this });
 *       wsServer.on('connection', onConnection);
 *     });
 *
 *     function onConnection(conn) {
 *       var stream = new ShareJSStream(conn);
 *       shareServer.listen(stream);
 *     }
 *
 * @class ShareJSStream
 * @extends Duplex
 * @constructor
 * @param {WebSocket} ws a websocket connection
 * @param {Object} [options] options for configuring the stream
 * @param {Number} [options.keepAlive=30000] a keep alive interval (in ms) on
 *   which the stream will send a `null` message to the client
 */
function ShareJSStream(ws, options) {
  options = options || {};
  options.keepAlive = options.keepAlive !== undefined ?
    options.keepAlive : KEEP_ALIVE;

  this.debug         = options.debug === true;
  this.ws            = ws;
  this.headers       = this.ws.upgradeReq.headers;
  this.remoteAddress = this.ws.upgradeReq.connection.remoteAddress;

  if (this.debug) {
    this.logger = logfmt.namespace({ ns: 'share-js-stream' });
  }

  Duplex.call(this, { objectMode: true });

  /**
   * Send a `null` message to the ws so that the connection is kept alive.
   *
   * This may not be necessary on all platforms.
   *
   * @method keepAlive
   * @private
   */
  this.keepAlive = function keepAlive() {
    this.log({ evt: 'keepAlive' });
    this.ws.send(null);
  }.bind(this);

  /**
   * Handle a closed ws client.
   *
   * This is called when the `close` event is emitted on the ws client. It:
   *
   * - Clears the keep alive interval (if there is one)
   * - Pushes `null` to end the stream
   * - Emits `close` on the stream
   * - Closes the ws client
   *
   * @method onWsClose
   * @private
   * @param {Number} code the reason code for why the client was closed
   * @param {String} message a message accompanying the close
   */
  this.onWsClose = function onWsClose(code, message) {
    if (this.keepAliveInterval) {
      clearInterval(this.keepAliveInterval);
    }

    this.log({ evt: 'wsClose', code: code, message: message });
    this.push(null);
    this.emit('close');
  }.bind(this);

  /**
   * Push a message received on the ws client as an object to the stream.
   *
   * @method onWsMessage
   * @private
   * @param {String} msg a JSON-encoded message received on the ws client
   */
  this.onWsMessage = function onWsMessage(msg) {
    this.log({ evt: 'wsMessage', msg: msg });

    try {
      msg = JSON.parse(msg);
    } catch(err) {
      this.emit('error', new Error('Client sent invalid JSON'));
      return;
    }

    this.push(msg);
  }.bind(this);

  /**
   * Handle the stream ending by closing the ws client connection.
   *
   * @method onStreamEnd
   * @private
   */
  this.onStreamEnd = function onStreamEnd() {
    this.log({ evt: 'streamEnd' });
    this.ws.close(1000);
  }.bind(this);

  /**
   * Handle a stream error by closing the ws client connection.
   *
   * @method onStreamError
   * @private
   * @param {Error} err the error emitted on the stream
   */
  this.onStreamError = function onStreamError(err) {
    this.log({ evt: 'streamError', err: err.message });
    this.ws.close(1008, err.message);
  }.bind(this);

  this.ws.on('close',   this.onWsClose);
  this.ws.on('message', this.onWsMessage);

  this.on('error', this.onStreamError);
  this.on('end',   this.onStreamEnd);

  if (options.keepAlive) {
    this.keepAliveInterval = setInterval(this.keepAlive, options.keepAlive);
  }
}

inherits(ShareJSStream, Duplex);

/**
 * If `debug` is true, log a message.
 *
 * @method log
 * @private
 * @param {Object} msg the message object to log
 */
ShareJSStream.prototype.log = function log(msg) {
  if (this.debug) {
    this.logger.log(msg);
  }
};

/**
 * Send a JSON-encoded message to the ws client.
 *
 * @method messageWsClient
 * @private
 * @param {Object} msg the message to send to the client
 */
ShareJSStream.prototype.messageWsClient = function messageWsClient(msg) {
  msg = JSON.stringify(msg);
  this.ws.send(msg);
};

/**
 * A no-op read operation for the stream.
 *
 * This is a no-op because _write immediately messages the ws client, rather
 * than pushing the message into a queue.
 *
 * @method _read
 * @private
 */
ShareJSStream.prototype._read = function _read() {
  // no-op
};

/**
 * Write a message to the stream.
 *
 * @method _write
 * @private
 * @param {Object} msg a message to be written to the stream
 * @param {String} encoding the encoding type (ignored)
 * @param {Function} cb a callback called when the `msg` is written
 */
ShareJSStream.prototype._write = function _write(msg, encoding, cb) {
  this.messageWsClient(msg);
  cb();
};

module.exports = ShareJSStream;
<commit_msg>Remove unnecessary `#messageWsClient` method<commit_after>'use strict';

var KEEP_ALIVE = 30 * 1000;
var Duplex     = require('stream').Duplex;
var inherits   = require('util').inherits;
var logfmt     = require('logfmt');

/**
 * A Stream for managing communication between a ShareJS client and a ws client
 *
 *     var ShareJSStream = require('share-js-stream');
 *     var WsServer      = require('ws').Server;
 *     var http          = require('http');
 *     var livedb        = require('livedb');
 *     var shareServer   = require('share').server.createClient({
 *       backend: livedb.client(livedb.memory())
 *     });
 *
 *     http.createServer().listen(process.env.PORT, function onListen() {
 *       var wsServer = new WsServer({ server: this });
 *       wsServer.on('connection', onConnection);
 *     });
 *
 *     function onConnection(conn) {
 *       var stream = new ShareJSStream(conn);
 *       shareServer.listen(stream);
 *     }
 *
 * @class ShareJSStream
 * @extends Duplex
 * @constructor
 * @param {WebSocket} ws a websocket connection
 * @param {Object} [options] options for configuring the stream
 * @param {Number} [options.keepAlive=30000] a keep alive interval (in ms) on
 *   which the stream will send a `null` message to the client
 */
function ShareJSStream(ws, options) {
  options = options || {};
  options.keepAlive = options.keepAlive !== undefined ?
    options.keepAlive : KEEP_ALIVE;

  this.debug         = options.debug === true;
  this.ws            = ws;
  this.headers       = this.ws.upgradeReq.headers;
  this.remoteAddress = this.ws.upgradeReq.connection.remoteAddress;

  if (this.debug) {
    this.logger = logfmt.namespace({ ns: 'share-js-stream' });
  }

  Duplex.call(this, { objectMode: true });

  /**
   * Send a `null` message to the ws so that the connection is kept alive.
   *
   * This may not be necessary on all platforms.
   *
   * @method keepAlive
   * @private
   */
  this.keepAlive = function keepAlive() {
    this.log({ evt: 'keepAlive' });
    this.ws.send(null);
  }.bind(this);

  /**
   * Handle a closed ws client.
   *
   * This is called when the `close` event is emitted on the ws client. It:
   *
   * - Clears the keep alive interval (if there is one)
   * - Pushes `null` to end the stream
   * - Emits `close` on the stream
   * - Closes the ws client
   *
   * @method onWsClose
   * @private
   * @param {Number} code the reason code for why the client was closed
   * @param {String} message a message accompanying the close
   */
  this.onWsClose = function onWsClose(code, message) {
    if (this.keepAliveInterval) {
      clearInterval(this.keepAliveInterval);
    }

    this.log({ evt: 'wsClose', code: code, message: message });
    this.push(null);
    this.emit('close');
  }.bind(this);

  /**
   * Push a message received on the ws client as an object to the stream.
   *
   * @method onWsMessage
   * @private
   * @param {String} msg a JSON-encoded message received on the ws client
   */
  this.onWsMessage = function onWsMessage(msg) {
    this.log({ evt: 'wsMessage', msg: msg });

    try {
      msg = JSON.parse(msg);
    } catch(err) {
      this.emit('error', new Error('Client sent invalid JSON'));
      return;
    }

    this.push(msg);
  }.bind(this);

  /**
   * Handle the stream ending by closing the ws client connection.
   *
   * @method onStreamEnd
   * @private
   */
  this.onStreamEnd = function onStreamEnd() {
    this.log({ evt: 'streamEnd' });
    this.ws.close(1000);
  }.bind(this);

  /**
   * Handle a stream error by closing the ws client connection.
   *
   * @method onStreamError
   * @private
   * @param {Error} err the error emitted on the stream
   */
  this.onStreamError = function onStreamError(err) {
    this.log({ evt: 'streamError', err: err.message });
    this.ws.close(1008, err.message);
  }.bind(this);

  this.ws.on('close',   this.onWsClose);
  this.ws.on('message', this.onWsMessage);

  this.on('error', this.onStreamError);
  this.on('end',   this.onStreamEnd);

  if (options.keepAlive) {
    this.keepAliveInterval = setInterval(this.keepAlive, options.keepAlive);
  }
}

inherits(ShareJSStream, Duplex);

/**
 * If `debug` is true, log a message.
 *
 * @method log
 * @private
 * @param {Object} msg the message object to log
 */
ShareJSStream.prototype.log = function log(msg) {
  if (this.debug) {
    this.logger.log(msg);
  }
};

/**
 * A no-op read operation for the stream.
 *
 * This is a no-op because _write immediately messages the ws client, rather
 * than pushing the message into a queue.
 *
 * @method _read
 * @private
 */
ShareJSStream.prototype._read = function _read() {
  // no-op
};

/**
 * Write a message to the stream, JSON-encoding it first.
 *
 * @method _write
 * @private
 * @param {Object} msg a message to be written to the stream
 * @param {String} encoding the encoding type (ignored)
 * @param {Function} cb a callback called when the `msg` is written
 */
ShareJSStream.prototype._write = function _write(msg, encoding, cb) {
  msg = JSON.stringify(msg);
  this.ws.send(msg);
  cb();
};

module.exports = ShareJSStream;
<|endoftext|>


<commit_before>'use strict';

var _          = require( 'lodash' ),
	gutil      = require( 'gulp-util' ),
	Transform  = require( 'stream' ).Transform,
	fork       = require( 'child_process' ).fork,
	pluginName = 'gulp-develop-server';

var isChanged = false;

var defaultOptions = {
	path: '',
	env: _.extend( { NODE_ENV: 'development' }, process.env ),
	execArgv: [],
	delay: 600,
	successMessage: /^[Ss]erver listening/,
	errorMessage: /[Ee]rror:/,
	killSignal: 'SIGTERM'
};

var app = module.exports = function( options ) {
	var stream = new Transform( { objectMode: true } ),
		isStream = false;

	// override default options
	if( ! app.child && typeof options === 'object' ) {
		_.merge( app.options, options );
	}

	stream._transform = function( file, encoding, callback ) {
		var pushFile = function() {
			stream.push( file );
			callback();
		};

		if( ! isStream ) {
			isStream = true;
			return app.changed( pushFile );
		}

		pushFile();
	};

	stream.on( 'finish', function() {
		isStream = false;
	});

	return stream;
};

var done = function( error, message, callback ) {

	// fallback arguments
	if( typeof message === 'function' ) {
		callback = message;
	}

	// print message
	else if( ! error && typeof message === 'string' ) {
		gutil.log( message );
	}

	// run callback
	if( typeof callback === 'function' ) {
		callback( error || null );
	}

	return app;
};

app.child = null;

app.options = _.cloneDeep( defaultOptions );

app.listen = function( options, callback ) {

	// throw error when options is not set
	if( ! app.options.path && typeof options.path !== 'string' ) {
		throw new gutil.pluginError( pluginName, 'application `path` required.' );
	}

	// fallback arguments
	if( typeof options === 'function' ) {
		callback = options;
		options = {};
	}

	// server already started
	if( app.child && app.child.connected ) {
		return done( 'Development server already started.', callback );
	}

	// override default options
	if( typeof options === 'object' ) {
		_.merge( app.options, options );
	}

	// run server process
	var child = fork( app.options.path, {
		cwd:      app.options.cwd,
		env:      app.options.env,
		encoding: app.options.encoding,
		execPath: app.options.execPath,
		execArgv: app.options.execArgv,
		silent:   true
	});

	// run callback when server initialized
	var called = false,
		timer;

	var initialized = function( error ) {
		if( called ) {
			return;
		}

		if( timer ) {
			timer = clearTimeout( timer );
		}

		if( error ) {
			gutil.log( gutil.colors.red( error ) );
			done( error, callback );
		}
		else {
			var pid = gutil.colors.magenta( child.pid );
			app.child = child;
			done( null, 'Development server listening. (PID:' + pid + ')', callback );
		}

		called = true;
		child.stderr.removeListener( 'data', errorListener );
		child.removeListener( 'message', successMessageListener );
	};

	// initialized by timer using `delay`
	if( app.options.delay > 0 ) {
		timer = setTimeout( initialized, app.options.delay );
	}

	// initialized by `successMessage`
	var successMessageListener = function( message ) {
		if( typeof message === 'string' && message.match( app.options.successMessage ) ) {
			initialized();
		}
	};
	child.on( 'message', successMessageListener );

	// initialized by `errorMessage` if server printed error
	var errorListener = function( error ) {
		if( error instanceof Buffer && error.toString().match( app.options.errorMessage ) ) {
			initialized( 'Development server has error.' );
		}
	};
	child.stderr.on( 'data', errorListener );

	// pipe child_process's stdout / stderr
	child.stdout.pipe( process.stdout );
	child.stderr.pipe( process.stderr );

	return app;
};

app.kill = function( signal, callback ) {

	// fallback arguments
	if( typeof signal === 'function' ) {
		callback = signal;
		signal = app.options.killSignal;
	}
	else {
		signal = signal || app.options.killSignal;
	}

	// send kill signal
	if( app.child ) {
		var stopped = function() {
			var pid = gutil.colors.magenta( app.child.pid );
			app.child = null;
			done( null, 'Development server was stopped. (PID:' + pid + ')', callback );
		};

		app.child.once( 'close', stopped );
		app.child.kill( signal );

		return app;
	}

	// server already stopped
	return done( 'Development server already stopped.', callback );
};

app.changed = app.restart = function( callback ) {

	// already called this function
	if( isChanged ) {
		isChanged = false;
		return done( null, 'Development server already received restart requests.', callback );
	}

	// restart server
	var restarted = function( error ) {
		isChanged = false;
		return done( error, gutil.colors.cyan( 'Development server was restarted.' ), callback );
	};

	isChanged = true;

	if( app.child ) {
		return app.kill( function() {
			app.listen( restarted );
		});
	}

	// if server not started, try to start using options.path
	else if( app.options.path ) {
		return app.listen( restarted );
	}

	// server not started
	throw new gutil.PluginError( pluginName, 'Development server not started.' );
};

app.reset = function( signal, callback ) {

	// fallback arguments
	if( typeof signal === 'function' ) {
		callback = signal;
		signal = app.options.killSignal;
	}
	else {
		signal = signal || app.options.killSignal;
	}

	// kill server process and then reset options
	var stopped = function( error ) {
		app.options = _.cloneDeep( defaultOptions );
		done( error, callback );
	};

	return app.kill( signal, stopped );
};
<commit_msg>Fix internal fork options<commit_after>'use strict';

var _          = require( 'lodash' ),
	gutil      = require( 'gulp-util' ),
	Transform  = require( 'stream' ).Transform,
	fork       = require( 'child_process' ).fork,
	pluginName = 'gulp-develop-server';

var isChanged = false;

var defaultOptions = {
	path: '',
	env: _.extend( { NODE_ENV: 'development' }, process.env ),
	execArgv: [],
	delay: 600,
	successMessage: /^[Ss]erver listening/,
	errorMessage: /[Ee]rror:/,
	killSignal: 'SIGTERM'
};

var app = module.exports = function( options ) {
	var stream = new Transform( { objectMode: true } ),
		isStream = false;

	// override default options
	if( ! app.child && typeof options === 'object' ) {
		_.merge( app.options, options );
	}

	stream._transform = function( file, encoding, callback ) {
		var pushFile = function() {
			stream.push( file );
			callback();
		};

		if( ! isStream ) {
			isStream = true;
			return app.changed( pushFile );
		}

		pushFile();
	};

	stream.on( 'finish', function() {
		isStream = false;
	});

	return stream;
};

var done = function( error, message, callback ) {

	// fallback arguments
	if( typeof message === 'function' ) {
		callback = message;
	}

	// print message
	else if( ! error && typeof message === 'string' ) {
		gutil.log( message );
	}

	// run callback
	if( typeof callback === 'function' ) {
		callback( error || null );
	}

	return app;
};

app.child = null;

app.options = _.cloneDeep( defaultOptions );

app.listen = function( options, callback ) {

	// throw error when options is not set
	if( ! app.options.path && typeof options.path !== 'string' ) {
		throw new gutil.pluginError( pluginName, 'application `path` required.' );
	}

	// fallback arguments
	if( typeof options === 'function' ) {
		callback = options;
		options = {};
	}

	// server already started
	if( app.child && app.child.connected ) {
		return done( 'Development server already started.', callback );
	}

	// override default options
	if( typeof options === 'object' ) {
		_.merge( app.options, options );
	}

	// run server process
	var child = fork( app.options.path, {
		cwd:      app.options.cwd,
		env:      app.options.env,
		execPath: app.options.execPath,
		execArgv: app.options.execArgv,
		gid:      app.options.gid,
		uid:      app.options.uid,
		silent:   true
	});

	// run callback when server initialized
	var called = false,
		timer;

	var initialized = function( error ) {
		if( called ) {
			return;
		}

		if( timer ) {
			timer = clearTimeout( timer );
		}

		if( error ) {
			gutil.log( gutil.colors.red( error ) );
			done( error, callback );
		}
		else {
			var pid = gutil.colors.magenta( child.pid );
			app.child = child;
			done( null, 'Development server listening. (PID:' + pid + ')', callback );
		}

		called = true;
		child.stderr.removeListener( 'data', errorListener );
		child.removeListener( 'message', successMessageListener );
	};

	// initialized by timer using `delay`
	if( app.options.delay > 0 ) {
		timer = setTimeout( initialized, app.options.delay );
	}

	// initialized by `successMessage`
	var successMessageListener = function( message ) {
		if( typeof message === 'string' && message.match( app.options.successMessage ) ) {
			initialized();
		}
	};
	child.on( 'message', successMessageListener );

	// initialized by `errorMessage` if server printed error
	var errorListener = function( error ) {
		if( error instanceof Buffer && error.toString().match( app.options.errorMessage ) ) {
			initialized( 'Development server has error.' );
		}
	};
	child.stderr.on( 'data', errorListener );

	// pipe child_process's stdout / stderr
	child.stdout.pipe( process.stdout );
	child.stderr.pipe( process.stderr );

	return app;
};

app.kill = function( signal, callback ) {

	// fallback arguments
	if( typeof signal === 'function' ) {
		callback = signal;
		signal = app.options.killSignal;
	}
	else {
		signal = signal || app.options.killSignal;
	}

	// send kill signal
	if( app.child ) {
		var stopped = function() {
			var pid = gutil.colors.magenta( app.child.pid );
			app.child = null;
			done( null, 'Development server was stopped. (PID:' + pid + ')', callback );
		};

		app.child.once( 'close', stopped );
		app.child.kill( signal );

		return app;
	}

	// server already stopped
	return done( 'Development server already stopped.', callback );
};

app.changed = app.restart = function( callback ) {

	// already called this function
	if( isChanged ) {
		isChanged = false;
		return done( null, 'Development server already received restart requests.', callback );
	}

	// restart server
	var restarted = function( error ) {
		isChanged = false;
		return done( error, gutil.colors.cyan( 'Development server was restarted.' ), callback );
	};

	isChanged = true;

	if( app.child ) {
		return app.kill( function() {
			app.listen( restarted );
		});
	}

	// if server not started, try to start using options.path
	else if( app.options.path ) {
		return app.listen( restarted );
	}

	// server not started
	throw new gutil.PluginError( pluginName, 'Development server not started.' );
};

app.reset = function( signal, callback ) {

	// fallback arguments
	if( typeof signal === 'function' ) {
		callback = signal;
		signal = app.options.killSignal;
	}
	else {
		signal = signal || app.options.killSignal;
	}

	// kill server process and then reset options
	var stopped = function( error ) {
		app.options = _.cloneDeep( defaultOptions );
		done( error, callback );
	};

	return app.kill( signal, stopped );
};
<|endoftext|>


<commit_before><commit_msg>getLength for non polygon and multiPolygon<commit_after><|endoftext|>


<commit_before>var util = require('util');
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
var Server = require('mongodb').Server;
var Base = require('./base');
var type = require('../data_type');
var log = require('../log');

var global = {};

var MongodbDriver = Base.extend({
  init: function(connection) {
    this._super();
    this.connection = connection;
  },

	/**
	 * Creates the migrations collection
	 *
	 * @param callback
	 */
	_createMigrationsCollection: function(callback) {
		this._run('createCollection', 'migrations', null, callback);
	},

	/**
	 * An alias for _createMigrationsCollection
	 */
	_createMigrationsTable: function(callback) {
		this._createMigrationsCollection(callback);
	},

  /**
	 * Creates a collection
	 *
	 * @param collectionName		- The name of the collection to be created
	 * @param callback
	 */
  createCollection: function(collectionName, callback) {
		this._run('createCollection', collectionName, null, callback);
  },

	/**
	 * An alias for createCollection
	 *
	 * @param collectionName		- The name of the collection to be created
	 * @param callback
	 */
	createTable: function(collectionName, callback) {
		this.createCollection(collectionName, callback);
	},

  /**
	 * Renames a collection
	 *
	 * @param collectionName		- The name of the existing collection to be renamed
	 * @param newCollectionName	- The new name of the collection
	 * @param callback
	 */
  renameCollection: function(collectionName, newCollectionName, callback) {
		this._run('renameCollection', collectionName, {newCollection: newCollectionName}, callback);
  },

  /**
	 * An alias for renameCollection
	 *
	 * @param collectionName		- The name of the existing collection to be renamed
	 * @param newCollectionName	- The new name of the collection
	 * @param callback
	 */
  renameTable: function(collectionName, newCollectionName, callback) {
		this.renameCollection(collectionName, newCollectionName, callback);
  },

  /**
	 * Drops a collection
	 *
	 * @param collectionName		- The name of the collection to be dropped
	 * @param callback
	 */
	dropCollection: function(collectionName, callback) {
		this._run('dropCollection', collectionName, null, callback);
	},

  /**
	 * An alias for dropCollection
	 *
	 * @param collectionName		- The name of the collection to be dropped
	 * @param callback
	 */
	dropTable: function(collectionName, callback) {
		this.dropCollection(collectionName, callback);
	},

	/**
	 * Adds an index to a collection
	 *
	 * @param collectionName	- The collection to add the index to
	 * @param indexName				- The name of the index to add
	 * @param columns					- The columns to add an index on
	 * @param	unique					- A boolean whether this creates a unique index
	 */
  addIndex: function(collectionName, indexName, columns, unique, callback) {

		var options = {
			indexName: indexName,
			columns: columns,
			unique: unique
		};

		this._run('createIndex', collectionName, options, callback);
  },

	/**
	 * Removes an index from a collection
	 *
	 * @param collectionName	- The collection to remove the index
	 * @param indexName				- The name of the index to remove
	 * @param columns
	 */
  removeIndex: function(collectionName, indexName, callback) {
		this._run('dropIndex', collectionName, {indexName: indexName}, callback);
	},

	/**
	 * Inserts a record(s) into a collection
	 *
	 * @param collectionName	- The collection to insert into
	 * @param toInsert				- The record(s) to insert
	 * @param callback
	 */
  insert: function(collectionName, toInsert, callback) {
		this._run('insert', collectionName, toInsert, callback);
  },

	/**
	 * Inserts a migration record into the migration collection
	 *
	 * @param name			- The name of the migration being run
	 * @param callback
	 */
  addMigrationRecord: function (name, callback) {
		this._run('insert', 'migrations', {name: name, run_on: new Date}, callback);
  },

	/**
	 * Runs a query
	 *
	 * @param collectionName	- The collection to query on
	 * @param query						- The query to run
	 * @param callback
	 */
	find: function(collectionName, query, callback) {
		this._run('find', collectionName, query, callback);
	},

	/**
	 * Gets all the collection names in mongo
	 *
	 * @param callback	- The callback to call with the collection names
	 */
	//_getCollectionNames: function(callback) {
	getCollectionNames: function(callback) {
		this._run('collections', null, null, callback);
	},

	/**
	 * Gets all the indexes for a specific collection
	 *
	 * @param collectionName	- The name of the collection to get the indexes for
	 * @param callback				- The callback to call with the collection names
	 */
	getIndexes: function(collectionName, callback) {
		this._run('indexInformation', collectionName, null, callback);
	},

	/**
	 * Gets a connection and runs a mongo command and returns the results
	 *
	 * @param command			- The command to run against mongo
	 * @param collection	- The collection to run the command on
	 * @param options			- An object of options to be used based on the command
	 * @arapm callback		- A callback to return the results
	 */
	_run: function(command, collection, options, callback) {

    var args = this._makeParamArgs(arguments);
    var callback = args[2];
    log.sql.apply(null, arguments);
    if(global.dryRun) {
      return callback();
    }

		var connectionString = 'mongodb://' + global.config.host + ':' + global.config.port + '/' + global.config.database;

		this.connection.connect(connectionString, function(err, db) {

			if(err) {
				return callback(err);
			}

			if(command === 'find') {
				// options is the query in this case
				db.collection(collection)[command](options).toArray(function(err, docs) {

					if(err) {
						return callback(err);
					}

					callback(null, docs);
					db.close();
				});
			}

			else if(command === 'renameCollection') {
				db[command](collection, options.newCollection, function(err, data) {

					if(err) {
						return callback(err);
					}

					callback(null, data);
					db.close();
				});
			} else if(command === 'createIndex') {

				db[command](collection, options.columns, {name: options.indexName, unique: options.unique}, function(err, data) {

					if(err) {
						return callback(err);
					}

					callback(null, data);
					db.close();
				});
			}
		  else if(command === 'dropIndex') {

		 		db.collection(collection)[command](options.indexName, function(err, data) {

					if(err) {
						return callback(err);
					}

					callback(null, data);
					db.close();
				});
			}
			else if(command === 'insert') {

				// options is the records to insert in this case
				db.collection(collection)[command](options, {}, function(err, docs) {

					if(err) {
						return callback(err);
					}

					callback(null, docs);
					db.close();
				});
			}

			else if(command === 'remove') {

				// options is the records to insert in this case
				db.collection(collection)[command]({name:options.toRemove}, function(err, docs) {

					if(err) {
						return callback(err);
					}

					callback(null, docs);
					db.close();
				});
			}
			else if(command === 'collections') {
				db.collections(function(err, collections) {

					if(err) {
						return callback(err);
					}

					callback(null, collections);
					db.close();
				});
			}
			else if(command === 'indexInformation') {
				db.indexInformation(collection, function(err, indexInfo) {

					if(err) {
						return callback(err);
					}

					callback(null, indexInfo);
					db.close();
				});
			}
			else {
				db[command](collection, function(err, data) {

					if(err) {
						return callback(err);
					}

					callback(null, data);
					db.close();
				});
			}
		});
	},

  _makeParamArgs: function(args) {
    var params = Array.prototype.slice.call(args);
    var sql = params.shift();
    var callback = params.pop();

    if (params.length > 0 && Array.isArray(params[0])) {
      params = params[0];
    }

    return [sql, params, callback];
  },

  all: function() {
		var args = this._makeParamArgs(arguments);
		return this.connection.query.apply(this.connection, args);
  },

	/**
	 * Queries the migrations collection
	 *
	 * @param callback
	 */
	_allLoadedMigrations: function(callback) {
		this._run('find', 'migrations', null, callback);
	},

	/**
	 * Deletes a migration
	 *
	 * @param migrationName	- The name of the migration to be deleted
	 */
	_deleteMigration: function(migrationName, callback) {
		this._run('remove', 'migrations', {toRemove: migrationName}, callback);
	},

	/**
	 * Closes the connection to mongodb
	 */
  close: function(callback) {
    this.connection.close(callback);
  }
});

/**
 * Gets a connection to mongo
 *
 * @param config		- The config to connect to mongo
 * @param callback	- The callback to call with a MongodbDriver object
 */
exports.connect = function(config, callback) {
  var db;
	var port;

	// Set the default port if one is not defined
	if(config.port === undefined) {
		config.port = '27017';
	}

	global.config = config;	// Expose this to the object. TODO Need to find a better way to do this.

  db = config.db || new MongoClient(new Server(config.server, port));
  callback(null, new MongodbDriver(db));
};
<commit_msg>Refactored _run method and mongodb connection<commit_after>var util = require('util');
var moment = require('moment');
var MongoClient = require('mongodb').MongoClient;
var Server = require('mongodb').Server;
var Base = require('./base');
var type = require('../data_type');
var log = require('../log');

var connectionString;

var MongodbDriver = Base.extend({

  init: function(connection, mongoString) {
    this._super();
    this.connection = connection;
		connectionString = mongoString;
  },

	/**
	 * Creates the migrations collection
	 *
	 * @param callback
	 */
	_createMigrationsCollection: function(callback) {
		this._run('createCollection', 'migrations', null, callback);
	},

	/**
	 * An alias for _createMigrationsCollection
	 */
	_createMigrationsTable: function(callback) {
		this._createMigrationsCollection(callback);
	},

  /**
	 * Creates a collection
	 *
	 * @param collectionName		- The name of the collection to be created
	 * @param callback
	 */
  createCollection: function(collectionName, callback) {
		this._run('createCollection', collectionName, null, callback);
  },

	/**
	 * An alias for createCollection
	 *
	 * @param collectionName		- The name of the collection to be created
	 * @param callback
	 */
	createTable: function(collectionName, callback) {
		this.createCollection(collectionName, callback);
	},

  /**
	 * Renames a collection
	 *
	 * @param collectionName		- The name of the existing collection to be renamed
	 * @param newCollectionName	- The new name of the collection
	 * @param callback
	 */
  renameCollection: function(collectionName, newCollectionName, callback) {
		this._run('renameCollection', collectionName, {newCollection: newCollectionName}, callback);
  },

  /**
	 * An alias for renameCollection
	 *
	 * @param collectionName		- The name of the existing collection to be renamed
	 * @param newCollectionName	- The new name of the collection
	 * @param callback
	 */
  renameTable: function(collectionName, newCollectionName, callback) {
		this.renameCollection(collectionName, newCollectionName, callback);
  },

  /**
	 * Drops a collection
	 *
	 * @param collectionName		- The name of the collection to be dropped
	 * @param callback
	 */
	dropCollection: function(collectionName, callback) {
		this._run('dropCollection', collectionName, null, callback);
	},

  /**
	 * An alias for dropCollection
	 *
	 * @param collectionName		- The name of the collection to be dropped
	 * @param callback
	 */
	dropTable: function(collectionName, callback) {
		this.dropCollection(collectionName, callback);
	},

	/**
	 * Adds an index to a collection
	 *
	 * @param collectionName	- The collection to add the index to
	 * @param indexName				- The name of the index to add
	 * @param columns					- The columns to add an index on
	 * @param	unique					- A boolean whether this creates a unique index
	 */
  addIndex: function(collectionName, indexName, columns, unique, callback) {

		var options = {
			indexName: indexName,
			columns: columns,
			unique: unique
		};

		this._run('createIndex', collectionName, options, callback);
  },

	/**
	 * Removes an index from a collection
	 *
	 * @param collectionName	- The collection to remove the index
	 * @param indexName				- The name of the index to remove
	 * @param columns
	 */
  removeIndex: function(collectionName, indexName, callback) {
		this._run('dropIndex', collectionName, {indexName: indexName}, callback);
	},

	/**
	 * Inserts a record(s) into a collection
	 *
	 * @param collectionName	- The collection to insert into
	 * @param toInsert				- The record(s) to insert
	 * @param callback
	 */
  insert: function(collectionName, toInsert, callback) {
		this._run('insert', collectionName, toInsert, callback);
  },

	/**
	 * Inserts a migration record into the migration collection
	 *
	 * @param name			- The name of the migration being run
	 * @param callback
	 */
  addMigrationRecord: function (name, callback) {
		this._run('insert', 'migrations', {name: name, run_on: new Date}, callback);
  },

	/**
	 * Runs a query
	 *
	 * @param collectionName	- The collection to query on
	 * @param query						- The query to run
	 * @param callback
	 */
	find: function(collectionName, query, callback) {
		this._run('find', collectionName, query, callback);
	},

	/**
	 * Gets all the collection names in mongo
	 *
	 * @param callback	- The callback to call with the collection names
	 */
	//_getCollectionNames: function(callback) {
	getCollectionNames: function(callback) {
		this._run('collections', null, null, callback);
	},

	/**
	 * Gets all the indexes for a specific collection
	 *
	 * @param collectionName	- The name of the collection to get the indexes for
	 * @param callback				- The callback to call with the collection names
	 */
	getIndexes: function(collectionName, callback) {
		this._run('indexInformation', collectionName, null, callback);
	},

	/**
	 * Gets a connection and runs a mongo command and returns the results
	 *
	 * @param command			- The command to run against mongo
	 * @param collection	- The collection to run the command on
	 * @param options			- An object of options to be used based on the command
	 * @arapm callback		- A callback to return the results
	 */
	_run: function(command, collection, options, callback) {

    var args = this._makeParamArgs(arguments);
    var callback = args[2];
    log.sql.apply(null, arguments);
    if(global.dryRun) {
      return callback();
    }

		// Get a connection to mongo
		this.connection.connect(connectionString, function(err, db) {

			if(err) {
				return callback(err);
			}

			// Callback function to return mongo records
			var callbackFunction = function(err, data) {
				if(err) {
					return callback(err);
				}
				callback(null, data);
				db.close();
			};

			// Depending on the command, we need to use different mongo methods
			switch(command) {
				case 'find':
					db.collection(collection)[command](options).toArray(callbackFunction);
					break;
				case 'renameCollection':
					db[command](collection, options.newCollection, callbackFunction);
					break;
				case 'createIndex':
					db[command](collection, options.columns, {name: options.indexName, unique: options.unique}, callbackFunction);
					break;
				case 'dropIndex':
			 		db.collection(collection)[command](options.indexName, callbackFunction);
					break;
				case 'insert':
					// options is the records to insert in this case
					db.collection(collection)[command](options, {}, callbackFunction);
					break;
				case 'remove':
					// options is the records to insert in this case
					db.collection(collection)[command]({name:options.toRemove}, callbackFunction);
					break;
				case 'collections':
					db.collections(callbackFunction);
					break;
				case 'indexInformation':
					db.indexInformation(collection, callbackFunction);
					break;
				default:
					db[command](collection, callbackFunction);
					break;
			}
		});
	},

  _makeParamArgs: function(args) {
    var params = Array.prototype.slice.call(args);
    var sql = params.shift();
    var callback = params.pop();

    if (params.length > 0 && Array.isArray(params[0])) {
      params = params[0];
    }

    return [sql, params, callback];
  },

  all: function() {
		var args = this._makeParamArgs(arguments);
		return this.connection.query.apply(this.connection, args);
  },

	/**
	 * Queries the migrations collection
	 *
	 * @param callback
	 */
	_allLoadedMigrations: function(callback) {
		this._run('find', 'migrations', null, callback);
	},

	/**
	 * Deletes a migration
	 *
	 * @param migrationName	- The name of the migration to be deleted
	 */
	_deleteMigration: function(migrationName, callback) {
		this._run('remove', 'migrations', {toRemove: migrationName}, callback);
	},

	/**
	 * Closes the connection to mongodb
	 */
  close: function(callback) {
    this.connection.close(callback);
  }
});

/**
 * Gets a connection to mongo
 *
 * @param config		- The config to connect to mongo
 * @param callback	- The callback to call with a MongodbDriver object
 */
exports.connect = function(config, callback) {
  var db;
	var port;
	var host;

	// Make sure the database is defined
	if(config.database === undefined) {
		throw new Error('database must be defined in database.json');
	}

	if(config.host === undefined) {
		host = 'localhost';
	}

	if(config.port === undefined) {
		port = 27017;
	}

	var mongoString = 'mongodb://' + host + ':' + port + '/' + config.database;

  db = config.db || new MongoClient(new Server(config.server, {port: port, host: host}));
  callback(null, new MongodbDriver(db, mongoString));
};
<|endoftext|>


<commit_before>const { IncomingWebhook } = require('@slack/client');
const humanizeDuration = require('humanize-duration');
const Octokit = require('@octokit/rest');
const config = require('./config.json');

module.exports.webhook = new IncomingWebhook(config.SLACK_WEBHOOK_URL);
module.exports.status = config.GC_SLACK_STATUS;

module.exports.getGithubCommit = async (build, octokit) => {
  try {
    const cloudSourceRepo = build.source.repoSource.repoName;
    const { commitSha } = build.sourceProvenance.resolvedRepoSource;

    // format github_ownerName_repoName
    const [, githubOwner, githubRepo] = cloudSourceRepo.split('_');

    // get github commit
    const githubCommit = await octokit.git.getCommit({
      commit_sha: commitSha,
      owner: githubOwner,
      repo: githubRepo,
    });

    // return github commit
    return githubCommit;
  } catch (err) {
    return err;
  }
};

// subscribe is the main function called by GCF.
module.exports.subscribe = async (event) => {
  try {
    const token = process.env.GITHUB_TOKEN;
    const octokit = token && new Octokit({
      auth: `token ${token}`,
    });
    const build = module.exports.eventToBuild(event.data);

    // Skip if the current status is not in the status list.
    const status = module.exports.status || ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT'];
    if (status.indexOf(build.status) === -1) {
      return;
    }

    const githubCommit = await module.exports.getGithubCommit(build, octokit);

    const message = await module.exports.createSlackMessage(build, githubCommit);
    // Send message to slack.
    module.exports.webhook.send(message);
  } catch (err) {
    module.exports.webhook.send(`Error: ${err}`);
  }
};

// eventToBuild transforms pubsub event message to a build object.
module.exports.eventToBuild = data => JSON.parse(Buffer.from(data, 'base64').toString());

const DEFAULT_COLOR = '#4285F4'; // blue
const STATUS_COLOR = {
  QUEUED: DEFAULT_COLOR,
  WORKING: DEFAULT_COLOR,
  SUCCESS: '#34A853', // green
  FAILURE: '#EA4335', // red
  TIMEOUT: '#FBBC05', // yellow
  INTERNAL_ERROR: '#EA4335', // red
};

// createSlackMessage create a message from a build object.
module.exports.createSlackMessage = async (build, githubCommit) => {
  const buildFinishTime = new Date(build.finishTime);
  const buildStartTime = new Date(build.startTime);

  const isWorking = build.status === 'WORKING';
  const timestamp = Math.round(((isWorking) ? buildStartTime : buildFinishTime).getTime() / 1000);

  const text = (isWorking)
    ? `Build \`${build.id}\` started`
    : `Build \`${build.id}\` finished`;

  const fields = [{
    title: 'Status',
    value: build.status,
  }];

  if (!isWorking) {
    const buildTime = humanizeDuration(buildFinishTime - buildStartTime);

    fields.push({
      title: 'Duration',
      value: buildTime,
    });
  }

  const message = {
    text,
    mrkdwn: true,
    attachments: [
      {
        color: STATUS_COLOR[build.status] || DEFAULT_COLOR,
        title: 'Build logs',
        title_link: build.logUrl,
        fields,
        footer: 'Google Cloud Build',
        footer_icon: 'https://ssl.gstatic.com/pantheon/images/containerregistry/container_registry_color.png',
        ts: timestamp,
      },
    ],
  };

  // Add source information to the message.
  const source = build.source || null;
  if (source) {
    message.attachments[0].fields.push({
      title: 'Repository',
      value: build.source.repoSource.repoName,
    });

    message.attachments[0].fields.push({
      title: 'Branch',
      value: build.source.repoSource.branchName,
    });

    if (githubCommit) {
      message.attachments[0].fields.push({
        title: 'Commit Author',
        value: githubCommit.data.author.name,
      });
    }
  }

  // Add images to the message.
  const images = build.images || [];
  // eslint-disable-next-line no-plusplus
  for (let i = 0, len = images.length; i < len; i++) {
    message.attachments[0].fields.push({
      title: 'Image',
      value: images[i],
    });
  }
  return message;
};
<commit_msg>Improve image(s) field and add safeguard for github app triggered builds<commit_after>const { IncomingWebhook } = require('@slack/client');
const humanizeDuration = require('humanize-duration');
const Octokit = require('@octokit/rest');
const config = require('./config.json');

module.exports.webhook = new IncomingWebhook(config.SLACK_WEBHOOK_URL);
module.exports.status = config.GC_SLACK_STATUS;

module.exports.getGithubCommit = async (build, octokit) => {
  try {
    const cloudSourceRepo = build.source.repoSource.repoName;
    const { commitSha } = build.sourceProvenance.resolvedRepoSource;

    // format github_ownerName_repoName
    const [, githubOwner, githubRepo] = cloudSourceRepo.split('_');

    // get github commit
    const githubCommit = await octokit.git.getCommit({
      commit_sha: commitSha,
      owner: githubOwner,
      repo: githubRepo,
    });

    // return github commit
    return githubCommit;
  } catch (err) {
    return err;
  }
};

// subscribe is the main function called by GCF.
module.exports.subscribe = async (event) => {
  try {
    const token = process.env.GITHUB_TOKEN;
    const octokit = token && new Octokit({
      auth: `token ${token}`,
    });
    const build = module.exports.eventToBuild(event.data);

    // Skip if the current status is not in the status list.
    const status = module.exports.status || ['SUCCESS', 'FAILURE', 'INTERNAL_ERROR', 'TIMEOUT'];
    if (status.indexOf(build.status) === -1) {
      return;
    }

    const githubCommit = await module.exports.getGithubCommit(build, octokit);

    const message = await module.exports.createSlackMessage(build, githubCommit);
    // Send message to slack.
    module.exports.webhook.send(message);
  } catch (err) {
    module.exports.webhook.send(`Error: ${err}`);
  }
};

// eventToBuild transforms pubsub event message to a build object.
module.exports.eventToBuild = data => JSON.parse(Buffer.from(data, 'base64').toString());

const DEFAULT_COLOR = '#4285F4'; // blue
const STATUS_COLOR = {
  QUEUED: DEFAULT_COLOR,
  WORKING: DEFAULT_COLOR,
  SUCCESS: '#34A853', // green
  FAILURE: '#EA4335', // red
  TIMEOUT: '#FBBC05', // yellow
  INTERNAL_ERROR: '#EA4335', // red
};

// createSlackMessage create a message from a build object.
module.exports.createSlackMessage = async (build, githubCommit) => {
  const buildFinishTime = new Date(build.finishTime);
  const buildStartTime = new Date(build.startTime);

  const isWorking = build.status === 'WORKING';
  const timestamp = Math.round(((isWorking) ? buildStartTime : buildFinishTime).getTime() / 1000);

  const text = (isWorking)
    ? `Build \`${build.id}\` started`
    : `Build \`${build.id}\` finished`;

  const fields = [{
    title: 'Status',
    value: build.status,
  }];

  if (!isWorking) {
    const buildTime = humanizeDuration(buildFinishTime - buildStartTime);

    fields.push({
      title: 'Duration',
      value: buildTime,
    });
  }

  const message = {
    text,
    mrkdwn: true,
    attachments: [
      {
        color: STATUS_COLOR[build.status] || DEFAULT_COLOR,
        title: 'Build logs',
        title_link: build.logUrl,
        fields,
        footer: 'Google Cloud Build',
        footer_icon: 'https://ssl.gstatic.com/pantheon/images/containerregistry/container_registry_color.png',
        ts: timestamp,
      },
    ],
  };

  // Add source information to the message.
  const { repoSource } = build.source || null;
  if (repoSource) {
    message.attachments[0].fields.push({
      title: 'Repository',
      value: repoSource.repoName,
    });

    message.attachments[0].fields.push({
      title: 'Branch',
      value: repoSource.branchName,
    });

    if (githubCommit) {
      message.attachments[0].fields.push({
        title: 'Commit Author',
        value: githubCommit.data.author.name,
      });
    }
  }

  // Add image(s) to the message.
  const images = build.images || [];
  if (images.length === 1) {
    message.attachments[0].fields.push({
      title: 'Image',
      value: images[0],
    });
  } else if (images.length > 1) {
    message.attachments[0].fields.push({
      title: 'Images',
      value: images.join('\n'),
    });
  }
  return message;
};
<|endoftext|>


<commit_before><commit_msg>Nullable argument now can be passed to observer.setBounds()<commit_after><|endoftext|>