<commit_before>exports = module.exports = new Build();

var path = require('path');
var fs = require('fs');
var Q = require('q');

function mkdir(dirPath) {
	var defer = Q.defer();
	fs.mkdir(dirPath, function() {
		defer.resolve(true);
	});
	return defer.promise;
}

function unloadModules(basePath) {
	var name = require.resolve('fire');
	if(typeof require.cache[name] != 'undefined') {
		delete require.cache[name];
	}

	var moduleName = require.resolve(path.join(basePath, 'index.js'));
	if(typeof require.cache[moduleName] != 'undefined') {
		delete require.cache[moduleName];
	}

	Object.keys(require.cache).forEach(function(requireName) {
		if(requireName.indexOf(basePath) === 0) {
			delete require.cache[requireName];
		}
	});
}

function loadApp(basePath, stage) {
	unloadModules(basePath);

	var fire = require('../../../');
	fire.stage = stage;
	fire.disabled = true;
	fire.appsContainerMap = {};

	require(path.join(basePath, 'index.js'));

	var appContainerIds = Object.keys(fire.appsContainerMap);
	if(appContainerIds.length > 1) {
		throw new Error('More than 1 app container found. This is currently not supported. You can create multiple apps by using the same id but different names. Please check `fire#app()`.');
	}
	if(!appContainerIds.length) {
		throw new Error('Could not find any app. Did you create an app in index.js by calling `fire#app()`? Is the current working directory correct?');
	}

	var appContainer = fire.appsContainerMap[appContainerIds[0]];
	return appContainer.getActiveApp();
}

function Build() {

}

Build.prototype.extendConfig = function(gruntConfig, grunt) {
	if(!gruntConfig.firebuild) {
		gruntConfig.firebuild = {};
	}

	gruntConfig.firebuild.migrations = {};
	gruntConfig.firebuild.api = {};
	gruntConfig.firebuild.scripts = {};
	gruntConfig.firebuild.procfile = {};
	gruntConfig.firebuild.version = {};

	var optionsApp = loadApp(process.cwd());

	if(optionsApp.container.numberOfApps() == 1) {
		gruntConfig.less = {
			options: {
				compress: true,
				sourceMap: false
			},
			src: {
				expand: true,
				src: ['./styles/*.less'],
				dest: '.fire/public/',
				ext: '.css'
			}
		};

		gruntConfig.jade = {
			compile: {
				options: {
					data: optionsApp._settings
				},
				files: [{
					expand: true,
					src: 'templates/**/*.jade',
					dest: '.fire/public/',
					ext: '.html'
				}]
			}
		};

		gruntConfig.uglify = {
			options: {
				sourceMap: true
			},
			build: {
				src: '.fire/public/scripts/fire.js',
				dest: '.fire/public/scripts/fire.min.js'
			}
		};
	}
	else {
		gruntConfig.less = {
			options: {
				compress: true,
				sourceMap: false
			},
			_shared: {
				dest: '.fire/public/styles/default.css',
				src: 'styles/_shared/default.less'
			}
		};

		gruntConfig.less[optionsApp.name] = {
			src: 'styles/' + optionsApp.name + '/default.less',
			dest: '.fire/public/' + optionsApp.name + '/styles/default.css'
		};

		gruntConfig.jade = {
			compile: {
				options: {
					data: optionsApp._settings
				},
				files: [{
					expand: true,
					cwd: 'templates/_shared/',
					src: '**/*.jade',
					dest: '.fire/public/' + optionsApp.name + '/templates/',
					ext: '.html'
				}, {
					expand: true,
					cwd: 'templates/' + optionsApp.name + '/',
					src: '**/*.jade',
					dest: '.fire/public/' + optionsApp.name + '/templates/',
					ext: '.html'
				}]
			}
		};

		gruntConfig.uglify = {
			options: {
				sourceMap: true
			},
			build: {
				src: '.fire/public/' + optionsApp.name + '/scripts/fire.js',
				dest: '.fire/public/' + optionsApp.name + '/scripts/fire.min.js'
			}
		};
	}

	if(!gruntConfig.release) {
		gruntConfig.release = {};
	}

	gruntConfig.release.migrate = {};
	gruntConfig.release.tests = {};

	if(!gruntConfig.run) {
		gruntConfig.run = {};
	}

	grunt.registerMultiTask('release', function(version) {
		var dotenv = require('dotenv');
		dotenv.load();

		var Migrations = require('../migrations');

		var basePath = process.cwd();
		var app = loadApp(basePath, 'release');

		app.models.forEach(function(model, modelName) {
			delete app.models.internals[modelName];
			delete app.models[modelName];
		});

		var done = this.async();

		if(this.target == 'tests') {
			app.controllers.setup(basePath)
				.then(function() {
					return app.models.setup(basePath);
				})
				.then(function() {
					return app.tests.setup(basePath);
				})
				.then(function() {
					return app.tests.createTests();
				})
				.then(done)
				.catch(done)
				.done();
		}
		else if(this.target == 'migrate') {
			var models = app.models;
			var migrations = new Migrations(app, models);

			var targetVersion = version;

			// Set-up without reading any of the models
			// We will create the models based on all migrations

			models.setup(null)
				.then(function() {
					return migrations.setup(app.container.numberOfApps() > 1 ? path.join(basePath, '.fire', 'migrations', app.name) : path.join(basePath, '.fire', 'migrations'));
				})
				.then(function() {
					if(migrations._.length === 0) {
						throw new Error('There are 0 migration files. Did you run `grunt build`?');
					}
				})
				.then(function() {
					// Let's find which database version we're at
					return migrations.currentVersion();
				})
				.then(function(currentVersion) {
					if(!targetVersion) {
						targetVersion = migrations._[migrations._.length - 1].version;
					}

					if(targetVersion == currentVersion) {
						console.log('*** Database is up-to-date.');
					}
					else {
						console.log('*** Migrating from `' + currentVersion + '` to `' + targetVersion + '`.');

						return migrations.migrate(currentVersion, targetVersion)
							.then(function() {
								return migrations.currentVersion();
							})
							.then(function(newVersion) {
								if(newVersion == targetVersion) {
									//everything went alright
									console.log('*** Migration successful to `' + targetVersion + '`.');
								}
								else {
									throw new Error('Database version after migration `' + newVersion + '` does not match target version `' + targetVersion + '`.');
								}
							});
					}
				})
				.then(function() {
					done();
				})
				.catch(function(error) {
					console.log(error.message);
					console.log(error.stack);

					throw error;
				})
				.done();
		}
	});

	grunt.registerMultiTask('firebuild', function() {
		var done = this.async();
		var basePath = process.cwd();
		var app = null;

		if(this.target == 'version') {
			var packageJSON = require(path.join(__dirname, '..', '..', '..', 'package.json'));

			fs.writeFile(path.join('.fire', 'VERSION'), packageJSON.version, function(error) {
				done(error);
			});
		}
		else if(this.target == 'procfile') {
			app = loadApp(basePath, 'build');

			var processes = [];
			processes.push('web: node index.js --web');

			if(app.workers.numberOfWorkers() > 0) {
				processes.push('worker: node index.js --workers');
			}

			if(app.schedulers.numberOfSchedulers() > 0) {
				processes.push('worker: node index.js --schedulers');
			}

			if(app.triggers.numberOfTriggers() > 0) {
				processes.push('worker: node index.js --triggers');
			}

			if(app.triggers.numberOfTriggers() > 0 || app.schedulers.numberOfSchedulers() > 0) {
				processes.push('clock: node index.js --clock');
			}

			fs.writeFile('Procfile', processes.join('\n'), function(error) {
				done(error);
			});
		}
		else if(this.target == 'migrations') {
			app = loadApp(basePath, 'build');

			mkdir(path.join(basePath, '.fire'))
				.then(function() {
					return mkdir(path.join(basePath, '.fire', 'migrations'));
				})
				.then(function() {
					if(app.container.numberOfApps() > 1) {
						return mkdir(path.join(basePath, '.fire', 'migrations', app.name));
					}
				})
				.then(function() {
					var Generate = require('../migrations/generate');
					var generate = new Generate(app, basePath);
					generate.delegate = {
						addMigration: function(fileName, stream) {
							var savePath = (app.container.numberOfApps() > 1 ? path.join(basePath, '.fire', 'migrations', app.name, fileName) : path.join(basePath, '.fire', 'migrations', fileName));

							var writeStream = fs.createWriteStream(savePath);
							stream.pipe(writeStream);

							writeStream.once('finish', function() {
								done();
							});
						}
					};

					return generate.createMigrations(true);
				})
				.then(function(result) {
					if(!result) {
						// If the result is false, there are no migrations so we'll call done.
						done();
					}
				})
				.done();
		}
		else if(this.target == 'api') {
			app = loadApp(basePath, 'build');

			app.controllers.setup(basePath)
				.then(function() {
					return app.models.setup(basePath);
				})
				.then(function() {
					return mkdir(path.join(basePath, '.fire'));
				})
				.then(function() {
					return mkdir(path.join(basePath, '.fire', 'api'));
				})
				.then(function() {
					if(app.container.numberOfApps() > 1) {
						return mkdir(path.join(basePath, '.fire', 'api', app.name));
					}
				})
				.then(function() {
					var result = Q.when(true);

					app.models.forEach(function(model) {
						result = result.then(function() {
							if(model.isShared() && !app.settings('isMaster')) {

							}
							else {
								var savePath = (app.container.numberOfApps() > 1) ? path.join(basePath, '.fire', 'api', app.name, model.getFileName() + '.js') : path.join(basePath, '.fire', 'api', model.getFileName() + '.js');
								var writeStream = fs.createWriteStream(savePath);
								return app.API.generateModelController(model, writeStream);
							}
						});
					});

					return result;
				})
				.then(function() {
					done();
				})
				.done();
		}
		else if(this.target == 'scripts') {
			app = loadApp(basePath, 'build');

			app.controllers.setup(basePath)
				.then(function() {
					return app.models.setup(basePath);
				})
				.then(function() {
					return app.tests.setup(basePath);
				})
				.then(function() {
					setImmediate(function() {
						return mkdir(path.join(basePath, '.fire'))
							.then(function() {
								return mkdir(path.join(basePath, '.fire', 'public'));
							})
							.then(function() {
								if(app.container.numberOfApps() > 1) {
									return mkdir(path.join(basePath, '.fire', 'public', app.name));
								}
							})
							.then(function() {
								if(app.container.numberOfApps() > 1) {
									return mkdir(path.join(basePath, '.fire', 'public', app.name, 'scripts'));
								}
								else {
									return mkdir(path.join(basePath, '.fire', 'public', 'scripts'));
								}
							})
							.then(function() {
								var savePath = (app.container.numberOfApps() > 1) ? path.join(basePath, '.fire', 'public', app.name, 'scripts', 'fire.js') : path.join(basePath, '.fire', 'public', 'scripts', 'fire.js');
								var writeStream = fs.createWriteStream(savePath);

								app.bridge.generate(writeStream)
									.then(function() {
										done();
									})
									.done();
							});
					});
				})
				.done();
		}
		else {
			done();
		}
	});

	grunt.registerTask('build', ['firebuild', 'less', 'jade', 'uglify']);
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-jade');
	return gruntConfig;
};
<commit_msg>exposes app in build module<commit_after>exports = module.exports = new Build();

var path = require('path');
var fs = require('fs');
var Q = require('q');

function mkdir(dirPath) {
	var defer = Q.defer();
	fs.mkdir(dirPath, function() {
		defer.resolve(true);
	});
	return defer.promise;
}

function unloadModules(basePath) {
	var name = require.resolve('fire');
	if(typeof require.cache[name] != 'undefined') {
		delete require.cache[name];
	}

	var moduleName = require.resolve(path.join(basePath, 'index.js'));
	if(typeof require.cache[moduleName] != 'undefined') {
		delete require.cache[moduleName];
	}

	Object.keys(require.cache).forEach(function(requireName) {
		if(requireName.indexOf(basePath) === 0) {
			delete require.cache[requireName];
		}
	});
}

function loadApp(basePath, stage) {
	unloadModules(basePath);

	var fire = require('../../../');
	fire.stage = stage;
	fire.disabled = true;
	fire.appsContainerMap = {};

	require(path.join(basePath, 'index.js'));

	var appContainerIds = Object.keys(fire.appsContainerMap);
	if(appContainerIds.length > 1) {
		throw new Error('More than 1 app container found. This is currently not supported. You can create multiple apps by using the same id but different names. Please check `fire#app()`.');
	}
	if(!appContainerIds.length) {
		throw new Error('Could not find any app. Did you create an app in index.js by calling `fire#app()`? Is the current working directory correct?');
	}

	var appContainer = fire.appsContainerMap[appContainerIds[0]];
	return appContainer.getActiveApp();
}

function Build() {
	this.app = null;
}

Build.prototype.extendConfig = function(gruntConfig, grunt) {
	if(!gruntConfig.firebuild) {
		gruntConfig.firebuild = {};
	}

	gruntConfig.firebuild.migrations = {};
	gruntConfig.firebuild.api = {};
	gruntConfig.firebuild.scripts = {};
	gruntConfig.firebuild.procfile = {};
	gruntConfig.firebuild.version = {};

	var optionsApp = loadApp(process.cwd());

	this.app = optionsApp;

	if(optionsApp.container.numberOfApps() == 1) {
		gruntConfig.less = {
			options: {
				compress: true,
				sourceMap: false
			},
			src: {
				expand: true,
				src: ['./styles/*.less'],
				dest: '.fire/public/',
				ext: '.css'
			}
		};

		gruntConfig.jade = {
			compile: {
				options: {
					data: optionsApp._settings
				},
				files: [{
					expand: true,
					src: 'templates/**/*.jade',
					dest: '.fire/public/',
					ext: '.html'
				}]
			}
		};

		gruntConfig.uglify = {
			options: {
				sourceMap: true
			},
			build: {
				src: '.fire/public/scripts/fire.js',
				dest: '.fire/public/scripts/fire.min.js'
			}
		};
	}
	else {
		gruntConfig.less = {
			options: {
				compress: true,
				sourceMap: false
			},
			_shared: {
				dest: '.fire/public/styles/default.css',
				src: 'styles/_shared/default.less'
			}
		};

		gruntConfig.less[optionsApp.name] = {
			src: 'styles/' + optionsApp.name + '/default.less',
			dest: '.fire/public/' + optionsApp.name + '/styles/default.css'
		};

		gruntConfig.jade = {
			compile: {
				options: {
					data: optionsApp._settings
				},
				files: [{
					expand: true,
					cwd: 'templates/_shared/',
					src: '**/*.jade',
					dest: '.fire/public/' + optionsApp.name + '/templates/',
					ext: '.html'
				}, {
					expand: true,
					cwd: 'templates/' + optionsApp.name + '/',
					src: '**/*.jade',
					dest: '.fire/public/' + optionsApp.name + '/templates/',
					ext: '.html'
				}]
			}
		};

		gruntConfig.uglify = {
			options: {
				sourceMap: true
			},
			build: {
				src: '.fire/public/' + optionsApp.name + '/scripts/fire.js',
				dest: '.fire/public/' + optionsApp.name + '/scripts/fire.min.js'
			}
		};
	}

	if(!gruntConfig.release) {
		gruntConfig.release = {};
	}

	gruntConfig.release.migrate = {};
	gruntConfig.release.tests = {};

	if(!gruntConfig.run) {
		gruntConfig.run = {};
	}

	grunt.registerMultiTask('release', function(version) {
		var dotenv = require('dotenv');
		dotenv.load();

		var Migrations = require('../migrations');

		var basePath = process.cwd();
		var app = loadApp(basePath, 'release');

		app.models.forEach(function(model, modelName) {
			delete app.models.internals[modelName];
			delete app.models[modelName];
		});

		var done = this.async();

		if(this.target == 'tests') {
			app.controllers.setup(basePath)
				.then(function() {
					return app.models.setup(basePath);
				})
				.then(function() {
					return app.tests.setup(basePath);
				})
				.then(function() {
					return app.tests.createTests();
				})
				.then(done)
				.catch(done)
				.done();
		}
		else if(this.target == 'migrate') {
			var models = app.models;
			var migrations = new Migrations(app, models);

			var targetVersion = version;

			// Set-up without reading any of the models
			// We will create the models based on all migrations

			models.setup(null)
				.then(function() {
					return migrations.setup(app.container.numberOfApps() > 1 ? path.join(basePath, '.fire', 'migrations', app.name) : path.join(basePath, '.fire', 'migrations'));
				})
				.then(function() {
					if(migrations._.length === 0) {
						throw new Error('There are 0 migration files. Did you run `grunt build`?');
					}
				})
				.then(function() {
					// Let's find which database version we're at
					return migrations.currentVersion();
				})
				.then(function(currentVersion) {
					if(!targetVersion) {
						targetVersion = migrations._[migrations._.length - 1].version;
					}

					if(targetVersion == currentVersion) {
						console.log('*** Database is up-to-date.');
					}
					else {
						console.log('*** Migrating from `' + currentVersion + '` to `' + targetVersion + '`.');

						return migrations.migrate(currentVersion, targetVersion)
							.then(function() {
								return migrations.currentVersion();
							})
							.then(function(newVersion) {
								if(newVersion == targetVersion) {
									//everything went alright
									console.log('*** Migration successful to `' + targetVersion + '`.');
								}
								else {
									throw new Error('Database version after migration `' + newVersion + '` does not match target version `' + targetVersion + '`.');
								}
							});
					}
				})
				.then(function() {
					done();
				})
				.catch(function(error) {
					console.log(error.message);
					console.log(error.stack);

					throw error;
				})
				.done();
		}
	});

	grunt.registerMultiTask('firebuild', function() {
		var done = this.async();
		var basePath = process.cwd();
		var app = null;

		if(this.target == 'version') {
			var packageJSON = require(path.join(__dirname, '..', '..', '..', 'package.json'));

			fs.writeFile(path.join('.fire', 'VERSION'), packageJSON.version, function(error) {
				done(error);
			});
		}
		else if(this.target == 'procfile') {
			app = loadApp(basePath, 'build');

			var processes = [];
			processes.push('web: node index.js --web');

			if(app.workers.numberOfWorkers() > 0) {
				processes.push('worker: node index.js --workers');
			}

			if(app.schedulers.numberOfSchedulers() > 0) {
				processes.push('worker: node index.js --schedulers');
			}

			if(app.triggers.numberOfTriggers() > 0) {
				processes.push('worker: node index.js --triggers');
			}

			if(app.triggers.numberOfTriggers() > 0 || app.schedulers.numberOfSchedulers() > 0) {
				processes.push('clock: node index.js --clock');
			}

			fs.writeFile('Procfile', processes.join('\n'), function(error) {
				done(error);
			});
		}
		else if(this.target == 'migrations') {
			app = loadApp(basePath, 'build');

			mkdir(path.join(basePath, '.fire'))
				.then(function() {
					return mkdir(path.join(basePath, '.fire', 'migrations'));
				})
				.then(function() {
					if(app.container.numberOfApps() > 1) {
						return mkdir(path.join(basePath, '.fire', 'migrations', app.name));
					}
				})
				.then(function() {
					var Generate = require('../migrations/generate');
					var generate = new Generate(app, basePath);
					generate.delegate = {
						addMigration: function(fileName, stream) {
							var savePath = (app.container.numberOfApps() > 1 ? path.join(basePath, '.fire', 'migrations', app.name, fileName) : path.join(basePath, '.fire', 'migrations', fileName));

							var writeStream = fs.createWriteStream(savePath);
							stream.pipe(writeStream);

							writeStream.once('finish', function() {
								done();
							});
						}
					};

					return generate.createMigrations(true);
				})
				.then(function(result) {
					if(!result) {
						// If the result is false, there are no migrations so we'll call done.
						done();
					}
				})
				.done();
		}
		else if(this.target == 'api') {
			app = loadApp(basePath, 'build');

			app.controllers.setup(basePath)
				.then(function() {
					return app.models.setup(basePath);
				})
				.then(function() {
					return mkdir(path.join(basePath, '.fire'));
				})
				.then(function() {
					return mkdir(path.join(basePath, '.fire', 'api'));
				})
				.then(function() {
					if(app.container.numberOfApps() > 1) {
						return mkdir(path.join(basePath, '.fire', 'api', app.name));
					}
				})
				.then(function() {
					var result = Q.when(true);

					app.models.forEach(function(model) {
						result = result.then(function() {
							if(model.isShared() && !app.settings('isMaster')) {

							}
							else {
								var savePath = (app.container.numberOfApps() > 1) ? path.join(basePath, '.fire', 'api', app.name, model.getFileName() + '.js') : path.join(basePath, '.fire', 'api', model.getFileName() + '.js');
								var writeStream = fs.createWriteStream(savePath);
								return app.API.generateModelController(model, writeStream);
							}
						});
					});

					return result;
				})
				.then(function() {
					done();
				})
				.done();
		}
		else if(this.target == 'scripts') {
			app = loadApp(basePath, 'build');

			app.controllers.setup(basePath)
				.then(function() {
					return app.models.setup(basePath);
				})
				.then(function() {
					return app.tests.setup(basePath);
				})
				.then(function() {
					setImmediate(function() {
						return mkdir(path.join(basePath, '.fire'))
							.then(function() {
								return mkdir(path.join(basePath, '.fire', 'public'));
							})
							.then(function() {
								if(app.container.numberOfApps() > 1) {
									return mkdir(path.join(basePath, '.fire', 'public', app.name));
								}
							})
							.then(function() {
								if(app.container.numberOfApps() > 1) {
									return mkdir(path.join(basePath, '.fire', 'public', app.name, 'scripts'));
								}
								else {
									return mkdir(path.join(basePath, '.fire', 'public', 'scripts'));
								}
							})
							.then(function() {
								var savePath = (app.container.numberOfApps() > 1) ? path.join(basePath, '.fire', 'public', app.name, 'scripts', 'fire.js') : path.join(basePath, '.fire', 'public', 'scripts', 'fire.js');
								var writeStream = fs.createWriteStream(savePath);

								app.bridge.generate(writeStream)
									.then(function() {
										done();
									})
									.done();
							});
					});
				})
				.done();
		}
		else {
			done();
		}
	});

	grunt.registerTask('build', ['firebuild', 'less', 'jade', 'uglify']);
	grunt.loadNpmTasks('grunt-contrib-less');
	grunt.loadNpmTasks('grunt-contrib-uglify');
	grunt.loadNpmTasks('grunt-contrib-jade');
	return gruntConfig;
};
<|endoftext|>


<commit_before>'use strict'

module.exports = function (dep) {
  const start = function (argv, binary, platform, device) {
    const { glob, shell, log, configProvider, bridge, portProvider, process, join, dateTime, cucumberHtmlReporter } = dep
    const patata = dep.patata.get()

    // Implementation files
    let includeFiles = glob
      .sync(`features/**/*.common.js`)
      .concat(glob.sync(`features/**/*.${platform}.js`))
      .concat(glob.sync(`hooks/**/*.common.js`))
      .concat(glob.sync(`hooks/**/*.${platform}.js`))

    // Component files
    let componentFiles = []

    // Fix tags
    let tags = argv.tags || ''
    tags = typeof tags === 'string' ? [tags] : tags

    // Tags for config
    let configTagsToParse = argv.configTags ? [argv.configTags] : tags
    let configTagsArray = []

    let extractTagsRegExp = /@([\w]+)/gi
    configTagsToParse.forEach((tags) => {
      var match = extractTagsRegExp.exec(tags)
      while (match != null) {
        configTagsArray.push(match[1])
        match = extractTagsRegExp.exec(tags)
      }
    })

    let config = configProvider.get(configTagsArray)

    // Reports
    const nowAsString = dateTime.dateTimeAsString()
    const report = {
      dirPath: join(process.cwd(), 'reports', nowAsString)
    }
    shell.mkdir('-p', report.dirPath)

    // Capability and Device
    let capability = { template: platform, append: {} }
    if (device) {
      capability.append.udid = device
    }

    log.debug('Capabilities', JSON.stringify(capability))

    portProvider.get().then((port) => {
      // Virtual suite
      let suite = {
        capability: capability,
        components: componentFiles,
        include: includeFiles,
        features: {
          tags: tags
        },
        provider: { path: binary },
        config: config,
        servers: [{ address: 'localhost', port: port }]
      }

      let onDoneArg = argv.onDone
      let onDoneFn = null
      if (onDoneArg) {
        let onDonePath = onDoneArg
        if (!onDoneArg.startsWith('/')) {
          onDonePath = join(process.cwd(), onDoneArg)
        }
        onDoneFn = require(onDonePath)
        log.log(`[Trigger]`, onDonePath)
      }

      const onDone = (success) => {
        try {
          if (typeof onDoneFn === 'function') {
            let {yargs} = dep
            let triggerOpts = { argv: yargs.argv, success, report, suite }
            onDoneFn(triggerOpts)
          }

          try {
            let jsonFilePath = join(report.dirPath, '/results.json')
            let htmlFilePath = join(report.dirPath, '/results.html')

            if (shell.test('-e', jsonFilePath)) {
              try {
                log.debug(`Parsing file ${jsonFilePath}`)
                require(jsonFilePath)

                log.debug(`Creating HTML report from "${jsonFilePath}" to "${htmlFilePath}"`)
                cucumberHtmlReporter.generate({
                  theme: 'bootstrap',
                  jsonFile: jsonFilePath,
                  output: htmlFilePath,
                  reportSuiteAsScenarios: true,
                  launchReport: false
                })
              } catch (ex) {

              }
            }
          } catch (ex) { }

          if (success) {
            log.log('Everything alright! Happy testing!')
          } else {
            log.warn('Finished with errors. Do not give up and cheer up!')
          }
        } catch (ex) {
          log.warn('Finished with errors. Do not give up and cheer up!')
        } finally {
          process.exit(success ? 0 : 1)
        }
      }

      let suiteName = 'run'
      patata.suite(suiteName, suite)
      bridge.attach(argv, patata, onDone, suiteName, platform, report)
    }).catch(function (err) {
      log.exit(err)
    })
  }

  return { start }
}
<commit_msg>Bugfix #8: do not run without tags<commit_after>'use strict'

module.exports = function (dep) {
  const start = function (argv, binary, platform, device) {
    const { glob, shell, log, configProvider, bridge, portProvider, process, join, dateTime, cucumberHtmlReporter } = dep
    const patata = dep.patata.get()

    // Implementation files
    let includeFiles = glob
      .sync(`features/**/*.common.js`)
      .concat(glob.sync(`features/**/*.${platform}.js`))
      .concat(glob.sync(`hooks/**/*.common.js`))
      .concat(glob.sync(`hooks/**/*.${platform}.js`))

    // Component files
    let componentFiles = []

    // Fix tags
    let tags = argv.tags
    tags = (typeof tags === 'string' ? [tags] : tags) || []

    // Tags for config
    let configTagsToParse = argv.configTags ? [argv.configTags] : tags
    let configTagsArray = []

    let extractTagsRegExp = /@([\w]+)/gi
    configTagsToParse.forEach((tags) => {
      var match = extractTagsRegExp.exec(tags)
      while (match != null) {
        configTagsArray.push(match[1])
        match = extractTagsRegExp.exec(tags)
      }
    })

    let config = configProvider.get(configTagsArray)

    // Reports
    const nowAsString = dateTime.dateTimeAsString()
    const report = {
      dirPath: join(process.cwd(), 'reports', nowAsString)
    }
    shell.mkdir('-p', report.dirPath)

    // Capability and Device
    let capability = { template: platform, append: {} }
    if (device) {
      capability.append.udid = device
    }

    log.debug('Capabilities', JSON.stringify(capability))

    portProvider.get().then((port) => {
      // Virtual suite
      let suite = {
        capability: capability,
        components: componentFiles,
        include: includeFiles,
        features: {
          tags: tags
        },
        provider: { path: binary },
        config: config,
        servers: [{ address: 'localhost', port: port }]
      }

      let onDoneArg = argv.onDone
      let onDoneFn = null
      if (onDoneArg) {
        let onDonePath = onDoneArg
        if (!onDoneArg.startsWith('/')) {
          onDonePath = join(process.cwd(), onDoneArg)
        }
        onDoneFn = require(onDonePath)
        log.log(`[Trigger]`, onDonePath)
      }

      const onDone = (success) => {
        try {
          if (typeof onDoneFn === 'function') {
            let {yargs} = dep
            let triggerOpts = { argv: yargs.argv, success, report, suite }
            onDoneFn(triggerOpts)
          }

          try {
            let jsonFilePath = join(report.dirPath, '/results.json')
            let htmlFilePath = join(report.dirPath, '/results.html')

            if (shell.test('-e', jsonFilePath)) {
              try {
                log.debug(`Parsing file ${jsonFilePath}`)
                require(jsonFilePath)

                log.debug(`Creating HTML report from "${jsonFilePath}" to "${htmlFilePath}"`)
                cucumberHtmlReporter.generate({
                  theme: 'bootstrap',
                  jsonFile: jsonFilePath,
                  output: htmlFilePath,
                  reportSuiteAsScenarios: true,
                  launchReport: false
                })
              } catch (ex) {

              }
            }
          } catch (ex) { }

          if (success) {
            log.log('Everything alright! Happy testing!')
          } else {
            log.warn('Finished with errors. Do not give up and cheer up!')
          }
        } catch (ex) {
          log.warn('Finished with errors. Do not give up and cheer up!')
        } finally {
          process.exit(success ? 0 : 1)
        }
      }

      let suiteName = 'run'
      patata.suite(suiteName, suite)
      bridge.attach(argv, patata, onDone, suiteName, platform, report)
    }).catch(function (err) {
      log.exit(err)
    })
  }

  return { start }
}
<|endoftext|>


<commit_before>/*
 * Handlebars 辅助函数集
 * @author huntbao
 */
'use strict';
let Handlebars = require('handlebars');
let neiDbConst = require('../fb-modules/config/db.json');

/**
 * 不翻译被包裹的内容
 * @param {Object} options
 *
 *
 */
function raw(options){
    return options.fn();
}

Handlebars.registerHelper('raw', raw);

/**
 * 一些逻辑运算: '=='、'==='、'<'、'<='、'>'、'>='、'&&'、'||'
 */
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
    switch (operator) {
        case '==':
            return (v1 == v2) ? options.fn(this) : options.inverse(this);
        case '===':
            return (v1 === v2) ? options.fn(this) : options.inverse(this);
        case '<':
            return (v1 < v2) ? options.fn(this) : options.inverse(this);
        case '<=':
            return (v1 <= v2) ? options.fn(this) : options.inverse(this);
        case '>':
            return (v1 > v2) ? options.fn(this) : options.inverse(this);
        case '>=':
            return (v1 >= v2) ? options.fn(this) : options.inverse(this);
        case '&&':
            return (v1 && v2) ? options.fn(this) : options.inverse(this);
        case '||':
            return (v1 || v2) ? options.fn(this) : options.inverse(this);
        default:
            return options.inverse(this);
    }
});

/**
 * 获取以点号分隔字符串后的最后一项, 相当于根据路径取文件的扩展名
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{extname "hello.js"}}')() // => "js"
 *      Handlebars.compile('{{extname "hellojs"}}')() // => ""
 *      Handlebars.compile('{{extname "hellojs."}}')() // => ""
 *      Handlebars.compile('{{extname "hello.util.js"}}')() // => "js"
 */
function extname(str) {
    return str.substr(str.lastIndexOf('.')).substr(1);
}
Handlebars.registerHelper('extname', extname);

/**
 * 获取模型名称, 如果它是数组, 默认使用 `[]` 嵌套, 比如二维数组: String[][]
 * 或者使用 `List` 嵌套, 比如二维数组: List<List<String>>
 */
Handlebars.registerHelper('typeName', function (model, options) {
    var type;
    var arrDim;
    var useList;
    if (model.hasOwnProperty('type')) {
        // 模型中的用法: `{{typeName this useList=true}}`;
        type = model.type;
        arrDim = model.arrDim;
        if (options.hasOwnProperty('hash')) {
            useList = options.hash.useList;
        }
    } else if (model.hasOwnProperty('hash')) {
        // 接口返回参数中的用法: `{{typeName type=outputModel arrDim=outputModelArrDim useList=true}}`
        type = model.hash.type;
        arrDim = model.hash.arrDim;
        useList = model.hash.useList;
    }
    if (arrDim) {
        if (useList === true) {
            var name = [];
            var len = arrDim;
            while (len--) {
                name.push('List<');
            }
            name.push(type);
            len = arrDim;
            while (len--) {
                name.push('>');
            }
        } else {
            var name = [type];
            var len = arrDim;
            while (len--) {
                name.push('[]');
            }
        }
        return name.join('');
    } else {
        return type;
    }
});

/**
 * 中划线'-' 转下划线'_'
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{hyphenToUnderline "a---b-c"}}')() // => "a_b_c"
 *      Handlebars.compile('{{hyphenToUnderline "---a-b-c"}}')() // => "_a_b_c"
 *      Handlebars.compile('{{hyphenToUnderline "a-b-c--"}}')() // => "a_b_c_"
 */
function hyphenToUnderline(str){
    return str.split(/-+/).join('_');
}
Handlebars.registerHelper('hyphenToUnderline',hyphenToUnderline);

/**
 * 中划线'-'后的字符转大写
 * @param {string} str
 * @return {string}
 * @example
 *      Handlebars.compile('{{hyphenToCamel "--a-b-c--"}}')() // => "ABC"
 *      Handlebars.compile('{{hyphenToCamel "a---b---c"}}')() // => "ABC"
 *
 */
function hyphenToCamel(str) {
    return str.split('-').map(x=>{
        return x.charAt(0).toUpperCase() + x.slice(1);
    }).join('');
}
Handlebars.registerHelper('hyphenToCamel', hyphenToCamel);

/**
 * hyphenToCamel的反函数，将大写字符转为小写并以中划线'-'分开
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{camelToHyphen "AbcDefGhi"}}')() // => "abc-def-ghi"
 *      Handlebars.compile('{{camelToHyphen "abcDEF"}}')() // => "abc-d-e-f"
 *      Handlebars.compile('{{camelToHyphen "abc--DEF"}}')() // => "abc-d-e-f"
 *      Handlebars.compile('{{camelToHyphen "abc--DEF--"}}')() // => "abc-d-e-f-"
 *
 */
function camelToHyphen(str) {
    return  str.split(/([A-Z][^A-Z]*)/g).filter(x=>{return x!= ''})
                .map(x=>{return x.toLowerCase()}).join('-').replace(/-+/g, '-');
}
Handlebars.registerHelper('camelToHyphen', camelToHyphen);

/**
 * 首字母小写
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{lowerFirst "abcDEF"}}')() // => "abcDEF"
 *      Handlebars.compile('{{lowerFirst "AbcDEF"}}')() // => "abcDEF"
 *      Handlebars.compile('{{lowerFirst "-AbcDEF"}}')() // => "-AbcDEF"
 *      Handlebars.compile('{{lowerFirst "A"}}')() // => "a"
 *      Handlebars.compile('{{lowerFirst ""}}')() // => ""
 */
function lowerFirst(str) {
    return str.charAt(0).toLowerCase() + str.slice(1);
}
Handlebars.registerHelper('lowerFirst', lowerFirst);

/**
 * 将换行替换为逗号(默认)或者自定义分隔符
 */
Handlebars.registerHelper('noLineBreak', function (str, options) {
    var sep = options.hash.sep || ',';
    return str.replace(/\n/g, sep);
});

/**
 * 格式化注释, 在每一行的前面添加 ` * `
 */
Handlebars.registerHelper('prettifyComment', function (str) {
    var lines = str.split('\n');
    return ' * ' + lines.join('\n * ');
});

/**
 * 首字母大写
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{upperFirst "abcDEF"}}')() // => "AbcDEF"
 *      Handlebars.compile('{{upperFirst "AbcDEF"}}')() // => "AbcDEF"
 *      Handlebars.compile('{{upperFirst "-abcDEF"}}')() // => "-abcDEF"
 *      Handlebars.compile('{{upperFirst "a"}}')() // => "A"
 *      Handlebars.compile('{{upperFirst ""}}')() // => ""

 */
function upperFirst(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}
Handlebars.registerHelper('upperFirst', upperFirst);

Handlebars.registerHelper('iosProperty', function(datatype, hasPrefix){
    let isObject= function (obj) {
        return ({}).toString.call(obj).toLowerCase() === '[object object]';
    };
    let helper = function(field, prefix) {
        let noStar = null;
        let refName = null;
        if (field.itemIsArray) {
            noStar = 0;
            refName = 'strong';
        } else {
            switch (field.format) {
                case neiDbConst.MDL_FMT_NUMBER:
                case neiDbConst.MDL_FMT_BOOLEAN:
                    noStar = 1;
                    refName = "assign";
                    break;
                case neiDbConst.MDL_FMT_STRING:
                    refName = "copy";
                    noStar = 0;
                    break;
                case neiDbConst.MDL_FMT_HASH:
                    refName = "strong";
                    noStar = 0;
                    break;
                default:
                    break;
            }
        }
        let header= `/**\n *  ${field.description}\n */\n`;
        let type;
        if(field.format !== neiDbConst.MDL_FMT_HASH || !hasPrefix){
            type = field.type;
        }else{
            type = prefix + field.type;
        }
        return header + `@property (nonatomic, ${refName}) ${type} ${noStar?"":"*"}${field.name};`
    };

    if(isObject(datatype)) {
        return datatype.fields && datatype.fields.map(x=> {
              return helper(x,this.args.prefix);
          }).join('\n');
    }else if(Array.isArray(datatype)){
        return datatype.filter(x=>{
            return x.format == neiDbConst.MDL_FMT_HASH;
        }).map(x=> {
            return helper(x,this.args.prefix);
        })
    }
});

/**
 * 把 json 对象转为 string 形式
 */
Handlebars.registerHelper('JSONStringify', function (obj) {
    return JSON.stringify(obj, null, '\t');
});
<commit_msg>fixed typo<commit_after>/*
 * Handlebars 辅助函数集
 * @author huntbao
 */
'use strict';
let Handlebars = require('handlebars');
let neiDbConst = require('../fb-modules/config/db.json');

/**
 * 不翻译被包裹的内容
 * @param {Object} options
 *
 *
 */
function raw(options){
    return options.fn();
}

Handlebars.registerHelper('raw', raw);

/**
 * 一些逻辑运算: '=='、'==='、'<'、'<='、'>'、'>='、'&&'、'||'
 */
Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {
    switch (operator) {
        case '==':
            return (v1 == v2) ? options.fn(this) : options.inverse(this);
        case '===':
            return (v1 === v2) ? options.fn(this) : options.inverse(this);
        case '<':
            return (v1 < v2) ? options.fn(this) : options.inverse(this);
        case '<=':
            return (v1 <= v2) ? options.fn(this) : options.inverse(this);
        case '>':
            return (v1 > v2) ? options.fn(this) : options.inverse(this);
        case '>=':
            return (v1 >= v2) ? options.fn(this) : options.inverse(this);
        case '&&':
            return (v1 && v2) ? options.fn(this) : options.inverse(this);
        case '||':
            return (v1 || v2) ? options.fn(this) : options.inverse(this);
        default:
            return options.inverse(this);
    }
});

/**
 * 获取以点号分隔字符串后的最后一项, 相当于根据路径取文件的扩展名
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{extname "hello.js"}}')() // => "js"
 *      Handlebars.compile('{{extname "hellojs"}}')() // => ""
 *      Handlebars.compile('{{extname "hellojs."}}')() // => ""
 *      Handlebars.compile('{{extname "hello.util.js"}}')() // => "js"
 */
function extname(str) {
    return str.substr(str.lastIndexOf('.')).substr(1);
}
Handlebars.registerHelper('extname', extname);

/**
 * 获取模型名称, 如果它是数组, 默认使用 `[]` 嵌套, 比如二维数组: String[][]
 * 或者使用 `List` 嵌套, 比如二维数组: List<List<String>>
 */
Handlebars.registerHelper('typeName', function (model, options) {
    var type;
    var arrDim;
    var useList;
    if (model.hasOwnProperty('type')) {
        // 模型中的用法: `{{typeName this useList=true}}`;
        type = model.type;
        arrDim = model.arrDim;
        if (options.hasOwnProperty('hash')) {
            useList = options.hash.useList;
        }
    } else if (model.hasOwnProperty('hash')) {
        // 接口返回参数中的用法: `{{typeName type=outputModel arrDim=outputModelArrDim useList=true}}`
        type = model.hash.type;
        arrDim = model.hash.arrDim;
        useList = model.hash.useList;
    }
    if (arrDim) {
        if (useList === true) {
            var name = [];
            var len = arrDim;
            while (len--) {
                name.push('List<');
            }
            name.push(type);
            len = arrDim;
            while (len--) {
                name.push('>');
            }
        } else {
            var name = [type];
            var len = arrDim;
            while (len--) {
                name.push('[]');
            }
        }
        return name.join('');
    } else {
        return type;
    }
});

/**
 * 中划线'-' 转下划线'_'
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{hyphenToUnderline "a---b-c"}}')() // => "a_b_c"
 *      Handlebars.compile('{{hyphenToUnderline "---a-b-c"}}')() // => "_a_b_c"
 *      Handlebars.compile('{{hyphenToUnderline "a-b-c--"}}')() // => "a_b_c_"
 */
function hyphenToUnderline(str){
    return str.split(/-+/).join('_');
}
Handlebars.registerHelper('hyphenToUnderline',hyphenToUnderline);

/**
 * 中划线'-'后的字符转大写
 * @param {string} str
 * @return {string}
 * @example
 *      Handlebars.compile('{{hyphenToCamel "--a-b-c--"}}')() // => "ABC"
 *      Handlebars.compile('{{hyphenToCamel "a---b---c"}}')() // => "ABC"
 *
 */
function hyphenToCamel(str) {
    return str.split('-').map(x=>{
        return x.charAt(0).toUpperCase() + x.slice(1);
    }).join('');
}
Handlebars.registerHelper('hyphenToCamel', hyphenToCamel);

/**
 * hyphenToCamel的反函数，将大写字符转为小写并以中划线'-'分开
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{camelToHyphen "AbcDefGhi"}}')() // => "abc-def-ghi"
 *      Handlebars.compile('{{camelToHyphen "abcDEF"}}')() // => "abc-d-e-f"
 *      Handlebars.compile('{{camelToHyphen "abc--DEF"}}')() // => "abc-d-e-f"
 *      Handlebars.compile('{{camelToHyphen "abc--DEF--"}}')() // => "abc-d-e-f-"
 *
 */
function camelToHyphen(str) {
    return  str.split(/([A-Z][^A-Z]*)/g).filter(x=>{return x!= ''})
                .map(x=>{return x.toLowerCase()}).join('-').replace(/-+/g, '-');
}
Handlebars.registerHelper('camelToHyphen', camelToHyphen);

/**
 * 首字母小写
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{lowerFirst "abcDEF"}}')() // => "abcDEF"
 *      Handlebars.compile('{{lowerFirst "AbcDEF"}}')() // => "abcDEF"
 *      Handlebars.compile('{{lowerFirst "-AbcDEF"}}')() // => "-AbcDEF"
 *      Handlebars.compile('{{lowerFirst "A"}}')() // => "a"
 *      Handlebars.compile('{{lowerFirst ""}}')() // => ""
 */
function lowerFirst(str) {
    return str.charAt(0).toLowerCase() + str.slice(1);
}
Handlebars.registerHelper('lowerFirst', lowerFirst);

/**
 * 将换行替换为逗号(默认)或者自定义分隔符
 */
Handlebars.registerHelper('noLineBreak', function (str, options) {
    var sep = options.hash.sep || ',';
    return str.replace(/\n/g, sep);
});

/**
 * 格式化注释, 在每一行的前面添加 ` * `
 */
Handlebars.registerHelper('prettifyComment', function (str) {
    var lines = str.split('\n');
    return ' * ' + lines.join('\n * ');
});

/**
 * 首字母大写
 * @param {string} str
 * @return {string}
 *
 * @example
 *      Handlebars.compile('{{upperFirst "abcDEF"}}')() // => "AbcDEF"
 *      Handlebars.compile('{{upperFirst "AbcDEF"}}')() // => "AbcDEF"
 *      Handlebars.compile('{{upperFirst "-abcDEF"}}')() // => "-abcDEF"
 *      Handlebars.compile('{{upperFirst "a"}}')() // => "A"
 *      Handlebars.compile('{{upperFirst ""}}')() // => ""

 */
function upperFirst(str) {
    return str.charAt(0).toUpperCase() + str.slice(1);
}
Handlebars.registerHelper('upperFirst', upperFirst);

Handlebars.registerHelper('iosProperty', function(datatype, hasPrefix){
    let isObject= function (obj) {
        return ({}).toString.call(obj).toLowerCase() === '[object object]';
    };
    let helper = function(field, prefix) {
        let noStar = null;
        let refName = null;
        if (field.itemIsArray) {
            noStar = 0;
            refName = 'strong';
        } else {
            switch (field.format) {
                case neiDbConst.MDL_FMT_NUMBER:
                case neiDbConst.MDL_FMT_BOOLEAN:
                    noStar = 1;
                    refName = "assign";
                    break;
                case neiDbConst.MDL_FMT_STRING:
                    refName = "copy";
                    noStar = 0;
                    break;
                case neiDbConst.MDL_FMT_HASH:
                    refName = "strong";
                    noStar = 0;
                    break;
                default:
                    break;
            }
        }
        let header= `/**\n *  ${field.description}\n */\n`;
        let type;
        if(field.format !== neiDbConst.MDL_FMT_HASH || !hasPrefix){
            type = field.type;
        }else{
            type = prefix + field.type;
        }
        return header + `@property (nonatomic, ${refName}) ${type} ${noStar?"":"*"}${field.name};`
    };

    if(isObject(datatype)) {
        return datatype.fields && datatype.fields.map(x=> {
              return helper(x,this.args.prefix);
          }).join('\n');
    }else if(Array.isArray(datatype)){
        return datatype.map(x=> {
            return helper(x,this.args.prefix);
        }).join('\n');
    }
});

/**
 * 把 json 对象转为 string 形式
 */
Handlebars.registerHelper('JSONStringify', function (obj) {
    return JSON.stringify(obj, null, '\t');
});
<|endoftext|>


<commit_before>var base_output = require('../lib/base_output'),
  ssl_helper = require('../lib/ssl_helper'),
  amqp_driver = require('../lib/amqp_driver'),
  util = require('util'),
  logger = require('log4node'),
  error_buffer = require('../lib/error_buffer');

function OutputAmqp() {
  base_output.BaseOutput.call(this);
  this.mergeConfig(this.serializer_config('json_logstash'));
  this.mergeConfig(ssl_helper.config());
  this.mergeConfig(error_buffer.config(function() {
    return 'amqp to ' + this.host + ':' + this.port + ' exchange ' + this.exchange_name;
  }));
  this.mergeConfig({
    name: 'Ampq',
    host_field: 'host',
    port_field: 'port',
    required_params: ['exchange_name'],
    optional_params: ['topic', 'durable', 'retry_delay', 'heartbeat', 'username', 'password', 'vhost', 'persistent'],
    default_values: {
      'durable': true,
      'retry_delay': 3000,
      'heartbeat': 10,
      'persistent': false,
    },
    start_hook: this.start,

  });
}

util.inherits(OutputAmqp, base_output.BaseOutput);

OutputAmqp.prototype.start = function(callback) {
  this.amqp_url = amqp_driver.buildUrl(this);
  this.channel = undefined;
  logger.info('Start AMQP output to', this.amqp_url, 'exchange', this.exchange_name, 'topic', this.topic);

  this.connected_callback = function(channel) {
    channel.assertExchange(this.exchange_name, this.topic ? 'topic' : 'fanout', {durable: this.durable}, function(err) {
      if (err) {
        logger.error('Unable to create exchange', err);
      }
      else {
        this.channel = channel;
      }
    }.bind(this));
  }.bind(this);
  this.disconnected_callback = function() {
    this.channel = undefined;
  }.bind(this);
  this.amqp_logger = logger;

  this.driver = amqp_driver.createAmqpClient(this);

  callback();
};

OutputAmqp.prototype.process = function(data) {
  if (this.channel) {
    var options = {};
    if (this.persistent) {
      options.persistent = true;
    }
    this.channel.publish(this.exchange_name, this.topic || '', new Buffer(this.serialize_data(data)), options);
  }
};

OutputAmqp.prototype.close = function(callback) {
  logger.info('Closing AMQP output', this.amqp_url + ' exchange ' + this.exchange_name);
  this.driver.close(callback);
};

exports.create = function() {
  return new OutputAmqp();
};
<commit_msg>Amqp reconnect Issue (#59)<commit_after>var base_output = require('../lib/base_output'),
    ssl_helper = require('../lib/ssl_helper'),
    util = require('util'),
    logger = require('log4node'),
    amqplib = require('amqplib'),
    error_buffer = require('../lib/error_buffer');

function OutputAmqp() {
  base_output.BaseOutput.call(this);
  this.mergeConfig(this.serializer_config('json_logstash'));
  this.mergeConfig(ssl_helper.config());
  this.mergeConfig(error_buffer.config(function() {
    return 'amqp to ' + this.host + ':' + this.port + ' exchange ' + this.exchange_name;
  }));
  this.mergeConfig({
    name: 'Ampq',
    required_params: ['exchange_name','host','port'],
    optional_params: ['topic', 'durable', 'retry_delay', 'heartbeat', 'username', 'password', 'vhost', 'persistent','ssl','response'],
    default_values: {
      'durable': true,
      'retry_delay': 3000,
      'heartbeat': 10,
      'persistent': false,
      'ssl': false,
      'response': false
    },
    start_hook: this.start,

  });
}

util.inherits(OutputAmqp, base_output.BaseOutput);

OutputAmqp.prototype.start = function(callback) {
  this.buildUrl();
  this.connect();
  callback();
};

OutputAmqp.prototype.buildUrl = function() {
  this.url = (this.ssl ? 'amqps' : 'amqp') + '://';
  if (this.username && this.password) {
    this.url += this.username + ':' + this.password + '@';
  } else if(this.username) {
    this.url += this.username + '@';
  }
  this.url += this.host + ':' + this.port;
  if (this.vhost) {
    this.url += this.vhost;
  }
  if(this.hasOwnProperty('heartbeat')) {
    this.url += '?heartbeat=' + this.heartbeat;
  }
};

OutputAmqp.prototype.registerEvents = function(conn) {
  let self = this;

  conn.on('error',function(err){
    console.error(err);
  });

  conn.on('close', function() {
    self.channel = null;
    self.currentConnection = null;
    if(!self.closing) {
      console.log(`Connection lost.Will attempt to reconnect in ${self.retry_delay}`);
      self.reconnect();
    }
  });
};

OutputAmqp.prototype.registerChannelEvents = function(channel) {
  let self = this;

  channel.on('error',function(err){
    console.error(err);
  });

  channel.on('close', function() {
    self.channel = null;
    self.currentConnection = null;
    if(!self.closing) {
      console.log(`Connection lost.Will attempt to reconnect in ${self.retry_delay}`);
      setTimeout(self.reconnect.bind(self), self.retry_delay);
    }
  });

  channel.on('return', function(msg) {
    console.error(msg.fields.replyText);
    console.error(self.url);
    msg.content = JSON.parse(msg.content.toString());
    console.error(JSON.stringify(msg));
    // let content = msg.content;
    // let options = msg.properties;
    // for(let key in options) {
    //     if(!options[key]) {
    //         delete options[key];
    //     }
    // }
    // setTimeout(()=>{
    //     console.log('Sending again');
    //     self.publish(content,options);
    // },10000);
  });
};

OutputAmqp.prototype.publish = function(message,options){
  let self = this;
  if (this.channel && this.currentConnection) {
    if(Object.keys(message).length!==0) {
      this.channel.publish(this.exchange_name, this.topic || '', new Buffer.from(JSON.stringify(message)), options, ((err) => {
        if (err) return logger.error(err);
        logger.info(`Published to ${self.exchange_name}`);
      }));
    }
  }
};

OutputAmqp.prototype.reconnect = function() {
  let self = this;
  setTimeout(()=>{
    self.connect().then(() => {
      clearTimeout(self.reconnect);
    }).catch((err) => console.error(err));
  },self.retry_delay);
};

OutputAmqp.prototype.connect = function(){
  let self = this;
  return new Promise((resolve,reject) => {
    amqplib.connect(this.url + '?heartbeat=' + this.heartbeat , {}).then((conn) => {
      logger.info(`AMQP Connected to ${self.url}`);
      self.currentConnection = conn;
      self.registerEvents(conn);
      return conn.createConfirmChannel();
    }).then((channel) => {
      logger.info(`AMQP Channel created in ${self.url}`);
      self.channel = channel;
      self.registerChannelEvents(channel);
      channel.checkExchange(self.exchange_name).then(() => {
        resolve(1);
      }).catch(() => {
        logger.info(`AMQP Exchange ${self.exchange_name} asserted in ${self.url}`);
        return channel.assertExchange(self.exchange_name, self.topic ? 'topic' : 'fanout', {durable: self.durable});
      });
    }).then(() => {
      resolve(1);
    }).catch((err) => {
      logger.error(`AMQP Error in ${self.url} . ${err}`);
      if(err.code !== 'EHOSTUNREACH' || err.statusCode !== 403) {
        setTimeout(self.reconnect.bind(self), self.retry_delay);
      }
      reject(0);
    });
  });
};

OutputAmqp.prototype.process = async function(data) {
  data = await data;
  if(!data) return;
  let options = {};
  if(typeof data === 'object') {
    if(data.hasOwnProperty('type_pastash')) {
      delete data.type_pastash;
    }
    delete data.host;
    if(data.options_type) {
      options.type = data.options_type;
      delete data.options_type;
    }
  }
  if (this.channel && this.currentConnection) {
    if (this.persistent) {
      options.persistent = true;
    }
    if(this.parsed_url.params.options_header) {
      options.headers = {};
      if(Array.isArray(this.parsed_url.params.options_header)) {
        this.parsed_url.params.options_header.forEach((header) => {
            options.headers[header] = "true";
        });
      } else {
        options.headers[this.parsed_url.params.options_header] = "true";
      }
    }
    console.log('data: ',JSON.stringify(data));
    console.log('options: ',JSON.stringify(options));
    this.publish(data, options);
  }
};

OutputAmqp.prototype.close = function(callback) {
  this.closing = true;
  if(this.currentConnection) {
    this.currentConnection.close();
    logger.info('AMQP Close');
  }
  this.currentConnection = null;
  this.channel = null;
  callback();
};

exports.create = function() {
  return new OutputAmqp();
};
<|endoftext|>


<commit_before>Template.subZero.onCreated(function(){
  var self = this;
  var subs = FlowRouter.current().route.options.subs;

  _.each( subs, function(sub){
    if(_.isFunction(sub)){
      sub.call(self);
    }else{
      self.subscribe.apply(self, sub);
    }
  });
});
<commit_msg>Using Template intance tracker, tracker is now destroyed.<commit_after>Template.subZero.onCreated(function(){
  var self = this;
  var subs = FlowRouter.current().route.options.subs;
  var trackFun = FlowRouter.current().route.options.trackFun;

  if(trackFun) self.autorun(function(){
    trackFun.call(self);
  });

  _.each( subs, function(sub){
    self.subscribe.apply(self, sub);
  });
});
<|endoftext|>


<commit_before>var nodeUrl = require('url');
var request = require('request');
var serverSecrets;
try {
  // Everything that cannot be checked in but is useful server-side
  // is stored in this JSON data.
  serverSecrets = require('./secret.json');
} catch(e) { console.error('No secret data (secret.json, see server.js):', e); }

// data: {url}, JSON-serializable object.
// end: function(json), with json of the form:
//  - badges: list of objects of the form:
//    - link: target as a string URL.
//    - badge: shields image URL.
var suggest = function(data, end) {
  try {
    var url = nodeUrl.parse(data.url);
  } catch(e) { end({err:''+e}); return; }

  if (url.hostname === 'github.com') {
    github(url, end);
  }
};

var github = function(url, cb) {
  var badges = [];
  var userRepo = url.pathname.slice(1).split('/');
  var user = userRepo[0];
  var repo = userRepo[1];
  githubLicense(user, repo, function(err, badge) {
    if (err == null) {
      badges.push(badge);
    }
    cb({badges:badges});
  });
};

// user: eg, qubyte
// repo: eg, rubidium
// end: function(err, {link, badge})
var githubLicense = function(user, repo, end) {
  // Step 1: Get the repo's default branch.
  var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '';
  // Using our OAuth App secret grants us 5000 req/hour
  // instead of the standard 60 req/hour.
  if (serverSecrets) {
    apiUrl += '?client_id=' + serverSecrets.gh_client_id
      + '&client_secret=' + serverSecrets.gh_client_secret;
  }
  var badgeData = {text:['license',''], colorscheme:'red'};
  // A special User-Agent is required:
  // http://developer.github.com/v3/#user-agent-required
  request(apiUrl, { headers: { 'User-Agent': 'Shields.io' } }, function(err, res, buffer) {
    if (err != null) { end(err); return; }
    try {
      if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
      var data = JSON.parse(buffer);
      var defaultBranch = data.default_branch;
      // Step 2: Get the SHA-1 hash of the branch tip.
      var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '/branches/' + defaultBranch;
      if (serverSecrets) {
        apiUrl += '?client_id=' + serverSecrets.gh_client_id
          + '&client_secret=' + serverSecrets.gh_client_secret;
      }
      request(apiUrl, { headers: { 'User-Agent': 'Shields.io' } }, function(err, res, buffer) {
        if (err != null) { end(err); return; }
        try {
          if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
          var data = JSON.parse(buffer);
          var branchTip = data.commit.sha;
          // Step 3: Get the tree at the commit.
          var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '/git/trees/' + branchTip;
          if (serverSecrets) {
            apiUrl += '?client_id=' + serverSecrets.gh_client_id
              + '&client_secret=' + serverSecrets.gh_client_secret;
          }
          request(apiUrl, { headers: { 'User-Agent': 'Shields.io' } }, function(err, res, buffer) {
            if (err != null) { end(err); return; }
            try {
              if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
              var data = JSON.parse(buffer);
              var treeArray = data.tree;
              var licenseBlob;
              var licenseFilename;
              // Crawl each file in the root directory
              for (var i = 0; i < treeArray.length; i++) {
                if (treeArray[i].type != 'blob') {
                  continue;
                }
                if (treeArray[i].path.match(/(LICENSE|COPYING|COPYRIGHT).*/i)) {
                  licenseBlob = treeArray[i].sha;
                  licenseFilename = treeArray[i].path;
                  break;
                }
              }
              // Could not find license file
              if (!licenseBlob) { end(Error('no license file')); return; }

              // Step 4: Get the license blob.
              var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '/git/blobs/' + licenseBlob;
              var link = 'https://raw.githubusercontent.com/' +
                [user, repo, defaultBranch, licenseFilename].join('/');

              if (serverSecrets) {
                apiUrl += '?client_id=' + serverSecrets.gh_client_id
                  + '&client_secret=' + serverSecrets.gh_client_secret;
              }
              // Get the raw blob instead of JSON
              // https://developer.github.com/v3/media/
              request(apiUrl, { headers: { 'User-Agent': 'Shields.io', 'Accept': 'appplication/vnd.github.raw' } },
              function(err, res, buffer) {
                if (err != null) { end(err); return; }
                try {
                  if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
                  var license = guessLicense(buffer);
                  if (license) {
                    badgeData.text[1] = license;
                    end(null, {
                      link: link,
                      badge: shieldsBadge(badgeData),
                    });
                    return;
                  } else {
                    // Not a recognized license
                    end(Error('unknown license'));
                    return;
                  }
                } catch(e) { end(e); return; }
              });
            } catch(e) { end(e); return; }
          });
        } catch(e) { end(e); return; }
      });
    } catch(e) { end(e); return; }
  });
};

// Key phrases for common licenses
var licensePhrases = {
  'Apache 1.1': 'apache (software)? license,? (version)? 1\\.1',
  'Apache 2': 'apache (software)? license,? (version)? 2',
  'Original BSD': 'all advertising materials mentioning features or use of this software must display the following acknowledgement',
  'New BSD': 'may be used to endorse or promote products derived from this software without specific prior written permission',
  'BSD': 'redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met',
  'GPLv2': 'gnu general public license,? version 2',
  'GPLv3': 'gnu general public license,? version 3',
  'GPL': 'gnu general public license',
  'LGPLv2.0': 'gnu library general public license,? version 2',
  'LGPLv2.1': 'gnu lesser general public license,? version 2\\.1',
  'LGPLv3': 'gnu lesser general public license,? version 3',
  'LGPL': 'gnu (library|lesser) general public license',
  'MIT': '\\(?(mit|expat|x11)\\)? license|permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files',
  'MPL 1.1': 'mozilla public license,? (\\(MPL\\) )?(version |v|v\\.)?1\\.1',
  'MPL 2': 'mozilla public license,? (\\(MPL\\) )?(version |v|v\\.)?2',
  'MPL': 'mozilla public license',
  'CDDL': 'common development and distribution license',
  'Eclipse': 'eclipse public license',
  'Artistic': 'artistic license',
  'zlib': 'the origin of this software must not be misrepresented',
  'AGPLv1': 'affero general public license,? version 1',
  'AGPLv3': 'affero general public license,? version 3',
  'AGPL': 'affero general public license',
  'ISC': 'permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted',
  'CC0': 'cc0',
  'Unlicense': 'this is free and unencumbered software released into the public domain',
}

// Try to guess the license based on the text and return an abbreviated name (or null if not recognized).
var guessLicense = function(text) {
  var licenseCodes = Object.keys(licensePhrases);
  var licenseRegex;
  var spaceMetaRegex = new RegExp(' ', 'g');
  for (var i = 0; i < licenseCodes.length; i++) {
    // Spaces can be any whitespace
    licenseRegex = licensePhrases[licenseCodes[i]].replace(spaceMetaRegex, '\\s+');
    if (text.match(new RegExp(licenseRegex, 'i'))) {
      return licenseCodes[i];
    }
  }
  // Not a recognized license
  return null;
};

var shieldsBadge = function(badgeData) {
  return ('https://img.shields.io/badge/'
    + escapeField(badgeData.text[0])
    + '-' + escapeField(badgeData.text[1])
    + '-' + badgeData.colorscheme + '.svg');
};

var escapeField = function(s) {
  return encodeURIComponent(s.replace(/-/g, '--').replace(/_/g, '__'));
};

module.exports = suggest;
<commit_msg>suggest.js: preprocess license regexes outside function<commit_after>var nodeUrl = require('url');
var request = require('request');
var serverSecrets;
try {
  // Everything that cannot be checked in but is useful server-side
  // is stored in this JSON data.
  serverSecrets = require('./secret.json');
} catch(e) { console.error('No secret data (secret.json, see server.js):', e); }

// data: {url}, JSON-serializable object.
// end: function(json), with json of the form:
//  - badges: list of objects of the form:
//    - link: target as a string URL.
//    - badge: shields image URL.
var suggest = function(data, end) {
  try {
    var url = nodeUrl.parse(data.url);
  } catch(e) { end({err:''+e}); return; }

  if (url.hostname === 'github.com') {
    github(url, end);
  }
};

var github = function(url, cb) {
  var badges = [];
  var userRepo = url.pathname.slice(1).split('/');
  var user = userRepo[0];
  var repo = userRepo[1];
  githubLicense(user, repo, function(err, badge) {
    if (err == null) {
      badges.push(badge);
    }
    cb({badges:badges});
  });
};

// user: eg, qubyte
// repo: eg, rubidium
// end: function(err, {link, badge})
var githubLicense = function(user, repo, end) {
  // Step 1: Get the repo's default branch.
  var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '';
  // Using our OAuth App secret grants us 5000 req/hour
  // instead of the standard 60 req/hour.
  if (serverSecrets) {
    apiUrl += '?client_id=' + serverSecrets.gh_client_id
      + '&client_secret=' + serverSecrets.gh_client_secret;
  }
  var badgeData = {text:['license',''], colorscheme:'red'};
  // A special User-Agent is required:
  // http://developer.github.com/v3/#user-agent-required
  request(apiUrl, { headers: { 'User-Agent': 'Shields.io' } }, function(err, res, buffer) {
    if (err != null) { end(err); return; }
    try {
      if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
      var data = JSON.parse(buffer);
      var defaultBranch = data.default_branch;
      // Step 2: Get the SHA-1 hash of the branch tip.
      var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '/branches/' + defaultBranch;
      if (serverSecrets) {
        apiUrl += '?client_id=' + serverSecrets.gh_client_id
          + '&client_secret=' + serverSecrets.gh_client_secret;
      }
      request(apiUrl, { headers: { 'User-Agent': 'Shields.io' } }, function(err, res, buffer) {
        if (err != null) { end(err); return; }
        try {
          if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
          var data = JSON.parse(buffer);
          var branchTip = data.commit.sha;
          // Step 3: Get the tree at the commit.
          var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '/git/trees/' + branchTip;
          if (serverSecrets) {
            apiUrl += '?client_id=' + serverSecrets.gh_client_id
              + '&client_secret=' + serverSecrets.gh_client_secret;
          }
          request(apiUrl, { headers: { 'User-Agent': 'Shields.io' } }, function(err, res, buffer) {
            if (err != null) { end(err); return; }
            try {
              if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
              var data = JSON.parse(buffer);
              var treeArray = data.tree;
              var licenseBlob;
              var licenseFilename;
              // Crawl each file in the root directory
              for (var i = 0; i < treeArray.length; i++) {
                if (treeArray[i].type != 'blob') {
                  continue;
                }
                if (treeArray[i].path.match(/(LICENSE|COPYING|COPYRIGHT).*/i)) {
                  licenseBlob = treeArray[i].sha;
                  licenseFilename = treeArray[i].path;
                  break;
                }
              }
              // Could not find license file
              if (!licenseBlob) { end(Error('no license file')); return; }

              // Step 4: Get the license blob.
              var apiUrl = 'https://api.github.com/repos/' + user + '/' + repo + '/git/blobs/' + licenseBlob;
              var link = 'https://raw.githubusercontent.com/' +
                [user, repo, defaultBranch, licenseFilename].join('/');

              if (serverSecrets) {
                apiUrl += '?client_id=' + serverSecrets.gh_client_id
                  + '&client_secret=' + serverSecrets.gh_client_secret;
              }
              // Get the raw blob instead of JSON
              // https://developer.github.com/v3/media/
              request(apiUrl, { headers: { 'User-Agent': 'Shields.io', 'Accept': 'appplication/vnd.github.raw' } },
              function(err, res, buffer) {
                if (err != null) { end(err); return; }
                try {
                  if ((+res.headers['x-ratelimit-remaining']) === 0) { end(Error('rate limited')); return; }
                  var license = guessLicense(buffer);
                  if (license) {
                    badgeData.text[1] = license;
                    end(null, {
                      link: link,
                      badge: shieldsBadge(badgeData),
                    });
                    return;
                  } else {
                    // Not a recognized license
                    end(Error('unknown license'));
                    return;
                  }
                } catch(e) { end(e); return; }
              });
            } catch(e) { end(e); return; }
          });
        } catch(e) { end(e); return; }
      });
    } catch(e) { end(e); return; }
  });
};

// Key phrases for common licenses
var licensePhrases = {
  'Apache 1.1': 'apache (software)? license,? (version)? 1\\.1',
  'Apache 2': 'apache (software)? license,? (version)? 2',
  'Original BSD': 'all advertising materials mentioning features or use of this software must display the following acknowledgement',
  'New BSD': 'may be used to endorse or promote products derived from this software without specific prior written permission',
  'BSD': 'redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met',
  'GPLv2': 'gnu general public license,? version 2',
  'GPLv3': 'gnu general public license,? version 3',
  'GPL': 'gnu general public license',
  'LGPLv2.0': 'gnu library general public license,? version 2',
  'LGPLv2.1': 'gnu lesser general public license,? version 2\\.1',
  'LGPLv3': 'gnu lesser general public license,? version 3',
  'LGPL': 'gnu (library|lesser) general public license',
  'MIT': '\\(?(mit|expat|x11)\\)? license|permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files',
  'MPL 1.1': 'mozilla public license,? (\\(MPL\\) )?(version |v|v\\.)?1\\.1',
  'MPL 2': 'mozilla public license,? (\\(MPL\\) )?(version |v|v\\.)?2',
  'MPL': 'mozilla public license',
  'CDDL': 'common development and distribution license',
  'Eclipse': 'eclipse public license',
  'Artistic': 'artistic license',
  'zlib': 'the origin of this software must not be misrepresented',
  'AGPLv1': 'affero general public license,? version 1',
  'AGPLv3': 'affero general public license,? version 3',
  'AGPL': 'affero general public license',
  'ISC': 'permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted',
  'CC0': 'cc0',
  'Unlicense': 'this is free and unencumbered software released into the public domain',
}
var licenseCodes = Object.keys(licensePhrases);
var spaceMetaRegex = new RegExp(' ', 'g');

// Spaces can be any whitespace
for (var i = 0; i < licenseCodes.length; i++) {
  licensePhrases[licenseCodes[i]] = licensePhrases[licenseCodes[i]].replace(spaceMetaRegex, '\\s+');
}

// Try to guess the license based on the text and return an abbreviated name (or null if not recognized).
var guessLicense = function(text) {
  var licenseRegex;
  for (var i = 0; i < licenseCodes.length; i++) {
    licenseRegex = licensePhrases[licenseCodes[i]];
    if (text.match(new RegExp(licenseRegex, 'i'))) {
      return licenseCodes[i];
    }
  }
  // Not a recognized license
  return null;
};

var shieldsBadge = function(badgeData) {
  return ('https://img.shields.io/badge/'
    + escapeField(badgeData.text[0])
    + '-' + escapeField(badgeData.text[1])
    + '-' + badgeData.colorscheme + '.svg');
};

var escapeField = function(s) {
  return encodeURIComponent(s.replace(/-/g, '--').replace(/_/g, '__'));
};

module.exports = suggest;
<|endoftext|>


<commit_before>/**
 * Basic Config Variables
 * redis_url (string) - Redis hostname (defaults to localhost)
 * ttl (int) - TTL on keys set in redis (defaults to 1 day)
 */
var redis_url = process.env.REDISTOGO_URL || process.env.REDISCLOUD_URL || process.env.REDISGREEN_URL || process.env.REDIS_URL || "redis://127.0.0.1:6379",
    url = require('url'),
    ttl = process.env.PAGE_TTL || 86400;

    // Parse out the connection vars from the env string.
    var connection = url.parse(redis_url),
    redis = require('redis'),
    client = redis.createClient(connection.port, connection.hostname),
    redis_online = false,
    last_error = "",
    last_end_message = ""; // Make redis connection

// Parse out password from the connection string
if (connection.auth) {
    client.auth(connection.auth.split(":")[1]);
}

// Catch all error handler. If redis breaks for any reason it will be reported here.
client.on("error", function (err) {
    if(last_error === err.toString()) {
      // Swallow the error for now
    } else {
      last_error = err.toString();
      console.log("Redis Cache Error: " + err);
    }
});
//
client.on("ready", function () {
    redis_online = true;
    console.log("Redis Cache Connected");
});

client.on("end", function (err) {
  if(err) {
    err = err.toString();
    if(last_end_message == err) {
      // Swallow the error for now
    } else {
      last_end_message = err;
      redis_online = false;
      console.log("Redis Cache Conncetion Closed. Will now bypass redis until it's back.");
    }
  }
});

module.exports = {
    beforePhantomRequest: function (req, res, next) {
        //
        if (req.method !== 'GET' || redis_online !== true) {
            return next();
        }

        client.get(req.prerender.url, function (err, result) {
            // Page found - return to prerender and 200
            if (!err && result) {
                res.send(200, result);
            } else {
                next();
            }
        });
    },

    afterPhantomRequest: function (req, res, next) {
        if (redis_online !== true) {
            return next();
        }
        // Don't cache anything that didn't result in a 200. This is to stop caching of 3xx/4xx/5xx status codes
        if (req.prerender.statusCode === 200) {
            client.set(req.prerender.url, req.prerender.documentHTML, function (err, reply) {
                // If library set to cache set an expiry on the key.
                if (!err && reply && ttl && ttl != 0) {
                    client.expire(req.prerender.url, ttl, function (err, didSetExpiry) {
                        console.warn(!!didSetExpiry);
                    });
                }
            });
        }
        next();
    }
};<commit_msg>added ping interval to prevent connection 'end' events<commit_after>/**
 * Basic Config Variables
 * redis_url (string) - Redis hostname (defaults to localhost)
 * ttl (int) - TTL on keys set in redis (defaults to 1 day)
 */
var redis_url = process.env.REDISTOGO_URL || process.env.REDISCLOUD_URL || process.env.REDISGREEN_URL || process.env.REDIS_URL || "redis://127.0.0.1:6379",
    url = require('url'),
    ttl = process.env.PAGE_TTL || 86400;

    // Parse out the connection vars from the env string.
    var connection = url.parse(redis_url),
    redis = require('redis'),
    client = redis.createClient(connection.port, connection.hostname),
    redis_online = false,
    last_error = "",
    last_end_message = ""; // Make redis connection

// Parse out password from the connection string
if (connection.auth) {
    client.auth(connection.auth.split(":")[1]);
}
// Ping redis every 180 seconds to keep connection alive
setInterval(client.ping(), 1000 * 180);

// Catch all error handler. If redis breaks for any reason it will be reported here.
client.on("error", function (err) {
    if(last_error === err.toString()) {
      // Swallow the error for now
    } else {
      last_error = err.toString();
      console.log("Redis Cache Error: " + err);
    }
});
//
client.on("ready", function () {
    redis_online = true;
    console.log("Redis Cache Connected");
});

client.on("end", function (err) {
  if(err) {
    err = err.toString();
    if(last_end_message == err) {
      // Swallow the error for now
    } else {
      last_end_message = err;
      redis_online = false;
      console.log("Redis Cache Conncetion Closed. Will now bypass redis until it's back.");
    }
  }
});

module.exports = {
    beforePhantomRequest: function (req, res, next) {
        //
        if (req.method !== 'GET' || redis_online !== true) {
            return next();
        }

        client.get(req.prerender.url, function (err, result) {
            // Page found - return to prerender and 200
            if (!err && result) {
                res.send(200, result);
            } else {
                next();
            }
        });
    },

    afterPhantomRequest: function (req, res, next) {
        if (redis_online !== true) {
            return next();
        }
        // Don't cache anything that didn't result in a 200. This is to stop caching of 3xx/4xx/5xx status codes
        if (req.prerender.statusCode === 200) {
            client.set(req.prerender.url, req.prerender.documentHTML, function (err, reply) {
                // If library set to cache set an expiry on the key.
                if (!err && reply && ttl && ttl != 0) {
                    client.expire(req.prerender.url, ttl, function (err, didSetExpiry) {
                        console.warn(!!didSetExpiry);
                    });
                }
            });
        }
        next();
    }
};
<|endoftext|>


<commit_before>var http = require('http');

var Headers = require('./headers').Headers;

var QUERY_STATE_CHECK_INTERVAL = 800; // 800ms

exports.version = 'unknown';

var Client = exports.Client = function(args){
  if (!args)
    args = {};
  // exports.version set in index.js of project root
  this.userAgent = 'presto-client-node ' + exports.version;

  this.host = args.host || 'localhost';
  this.port = args.port || 8080;
  this.user = args.user || process.env.USER;

  this.catalog = args.catalog;
  this.schema = args.schema;

  this.checkInterval = args.checkInterval || QUERY_STATE_CHECK_INTERVAL;
};

Client.prototype.request = function(opts, callback) {
  var client = this;

  var contentBody = null;
  if (opts instanceof Object) {
    opts.host = client.host;
    opts.port = client.port;

    if (! opts.headers)
      opts.headers = {};

    if (client.user)
      opts.headers[Headers.USER] = client.user;

    opts.headers[Headers.USER_AGENT] = this.userAgent;

    if (opts.body)
      contentBody = opts.body;
  }

  var req = http.request(opts, function(res){
    var response_code = res.statusCode;
    var response_data = '';
    res.setEncoding('utf8');
    res.on('data', function(chunk){
      response_data += chunk;
    });
    res.on('end', function(){
      var data = response_data;
      if (response_code < 300 && (data[0] === '{' || data[0] === '[')) {
        try { data = JSON.parse(data); }
        catch (x) {
          /* ignore json parse error (and don't parse) for non-json content body */
        }
      }
      callback(null, response_code, data);
    });
  });

  req.on('error', function(e){
    callback(e);
  });

  if (contentBody)
    req.write(contentBody);

  req.end();
};

Client.prototype.nodes = function(opts, callback) { // TODO: "failed" nodes not supported yet
  if (! callback) {
    callback = opts;
    opts = {};
  }

  this.request({ method: 'GET', path: '/v1/node' }, function(error, code, data){
    if (error || code !== 200) {
      var message = "node list api returns error" + (data && data.length > 0 ? ":" + data : "");
      callback({message: message, error: error, code: code});
      return;
    }
    callback(null, data);
  });
};

Client.prototype.query = function(query_id, callback) {
  this.request({ method: 'GET', path: '/v1/query/' + query_id }, function(error, code, data){
    if (error || code !== 200) {
      var message = "query info api returns error" + (data && data.length > 0 ? ":" + data : "");
      callback({message: message, error: error, code: code});
      return;
    }
    callback(null, data);
  });
};

Client.prototype.kill = function(query_id, callback) {
  this.request({ method: 'DELETE', path: '/v1/query/' + query_id }, function(error, code, data){
    if (error || code !== 204) {
      var message = "query kill api returns error" + (data && data.length > 0 ? ":" + data : "");
      callback({message: message, error: error, code: code});
      return;
    }
    if (callback)
      callback(null);
  });
};

Client.prototype.execute = function(opts, callback) {
  if (callback)
    this.executeResource(opts, callback);
  else
    this.statementResource(opts);
};

Client.prototype.executeResource = function(opts, callback) {
  var client = this;

  var query = null;
  if (opts instanceof Object) {
    query = opts.query;
  } else { // opts is query string itself
    query = opts;
    opts = {};
  }

  if (!opts.catalog && !this.catalog)
    throw {message: "catalog not specified"};
  if (!opts.schema && !this.schema)
    throw {message: "schema not specified"};

  var header = {};
  header[Headers.CATALOG] = opts.catalog || this.catalog;
  header[Headers.SCHEMA] = opts.schema || this.schema;

  var req = { method: 'POST', path: '/v1/execute', headers: header, body: query };
  client.request(req, function(err, code, content){
    if (err || code !== 200) {
      var message = "execution error" + (content && content.length > 0 ? ":" + content : "");
      callback({message:message, error: err, code: code});
      return;
    }
    // content: {"columns":[{"name":"Schema","type":"varchar"}],"data":[["default"],["information_schema"],["sys"]]}
    callback(null, content.data, content.columns);
  });
};

Client.prototype.statementResource = function(opts) {
  var client = this;
  var columns = null;

  if (!opts.catalog && !this.catalog)
    throw {message: "catalog not specified"};
  if (!opts.schema && !this.schema)
    throw {message: "schema not specified"};

  var header = {};
  header[Headers.CATALOG] = opts.catalog || this.catalog;
  header[Headers.SCHEMA] = opts.schema || this.schema;

  var fetch_info = opts.info || false;

  var cancel_checker = opts.cancel;
  var state_callback = opts.state;
  var columns_callback = opts.columns;
  var data_callback = opts.data;
  var success_callback = opts.success || opts.callback;
  var error_callback = opts.error || opts.callback;

  var req = { method: 'POST', path: '/v1/statement', headers: header, body: opts.query };
  client.request(req, function(err, code, data){
    if (err || code !== 200 || (data && data.error)) {
      if (error_callback) {
        var message = "execution error" + (data && data.length > 0 ? ":" + data : "");
        if (data && data.error && data.error.message)
          message = data.error.message;
        error_callback({message:message, error: (err || data.error), code: code});
      }
      return;
    }
    /*
    var data = {
      "stats": {
        "processedBytes": 0, "processedRows": 0,
        "wallTimeMillis": 0, "cpuTimeMillis": 0, "userTimeMillis": 0,
        "state": "QUEUED",
        "scheduled": false,
        "nodes": 0,
        "totalSplits": 0, "queuedSplits": 0, "runningSplits": 0, "completedSplits": 0,
      },
      "nextUri": "http://localhost:8080/v1/statement/20140120_032523_00000_32v8g/1",
      "infoUri": "http://localhost:8080/v1/query/20140120_032523_00000_32v8g",
      "id": "20140120_032523_00000_32v8g"
    };
     */
    if (!data.id || !data.nextUri || !data.infoUri) {
      var error_message = null;
      if (!data.id)
        error_message = "query id missing in response for POST /v1/statement";
      else if (!data.nextUri)
        error_message = "nextUri missing in response for POST /v1/statement";
      else if (!data.infoUri)
        error_message = "infoUri missing in response for POST /v1/statement";
      error_callback({message: error_message, data: data});
      return;
    }
    var last_state = null;
    var firstNextUri = data.nextUri; // TODO: check the cases without nextUri for /statement ?
    var fetch_next = function(next_uri){
      /*
       * 1st time
{
 "stats": {
   "rootStage": {
     "subStages": [
       {
         "subStages": [],
         "processedBytes": 83103149, "processedRows": 2532704,
         "wallTimeMillis": 20502, "cpuTimeMillis": 3431, "userTimeMillis": 3210,
         "stageId": "1", "state": "FINISHED", "done": true,
         "nodes": 3,
         "totalSplits": 420, "queuedSplits": 0, "runningSplits": 0, "completedSplits": 420
       }
     ],
     // same as substage
   },
   // same as substage
   "state": "RUNNING",
 },
 "data": [ [ 1266352 ] ],
 "columns": [ { "type": "bigint", "name": "cnt" } ],
 "nextUri": "http://localhost:8080/v1/statement/20140120_032523_00000_32v8g/2",
 "partialCancelUri": "http://10.0.0.0:8080/v1/stage/20140120_032523_00000_32v8g.0",
 "infoUri": "http://localhost:8080/v1/query/20140120_032523_00000_32v8g",
 "id": "20140120_032523_00000_32v8g"
}
       */
      /*
       * 2nd time
{
 "stats": {
   // ....
   "state": "FINISHED",
 },
 "columns": [ { "type": "bigint", "name": "cnt" } ],
 "infoUri": "http://localhost:8080/v1/query/20140120_032523_00000_32v8g",
 "id": "20140120_032523_00000_32v8g"
}
       */
      if (cancel_checker && cancel_checker()) {
        client.request({ method: 'DELETE', path: next_uri }, function(error, code, data){
          if (error || code !== 204) {
            error_callback({message: "query fetch canceled, but Presto query cancel may fail", error: error, code: code});
          } else {
            error_callback({message: "query fetch canceled by operation"});
          }
        });
        return;
      }
      client.request(next_uri, function(error, code, response){
        if (response.error) {
          error_callback(response.error);
          return;
        }

        if (state_callback && last_state !== response.stats.state) {
          state_callback(null, response.id, response.stats);
          last_state = response.stats.state;
        }

        if (columns_callback && response.columns && !columns) {
          columns = response.columns;
          columns_callback(null, columns);
        }

        /* presto-main/src/main/java/com/facebook/presto/execution/QueryState.java
         * QUEUED, PLANNING, STARTING, RUNNING, FINISHED, CANCELED, FAILED
         */
        if (response.stats.state === 'QUEUED'
          || response.stats.state === 'PLANNING'
          || response.stats.state === 'STARTING'
          || response.stats.state === 'RUNNING' && !response.data) {
          var next = response.nextUri;
          setTimeout(function(){ fetch_next(next); }, client.checkInterval);
          return;
        }

        if (data_callback && response.data) {
          data_callback(null, response.data, response.columns, response.stats);
        }

        if (response.nextUri) {
          var next = response.nextUri;
          setTimeout(function(){ fetch_next(next); }, client.checkInterval);
          return;
        }

        var finishedStats = response.stats;

        if (fetch_info && response.infoUri) {
          client.request(response.infoUri, function(error, code, response){
            success_callback(null, finishedStats, response);
          });
        }
        else {
          success_callback(null, finishedStats);
        }
      });
    };
    fetch_next(firstNextUri);
  });
};
<commit_msg>add check for required callback<commit_after>var http = require('http');

var Headers = require('./headers').Headers;

var QUERY_STATE_CHECK_INTERVAL = 800; // 800ms

exports.version = 'unknown';

var Client = exports.Client = function(args){
  if (!args)
    args = {};
  // exports.version set in index.js of project root
  this.userAgent = 'presto-client-node ' + exports.version;

  this.host = args.host || 'localhost';
  this.port = args.port || 8080;
  this.user = args.user || process.env.USER;

  this.catalog = args.catalog;
  this.schema = args.schema;

  this.checkInterval = args.checkInterval || QUERY_STATE_CHECK_INTERVAL;
};

Client.prototype.request = function(opts, callback) {
  var client = this;

  var contentBody = null;
  if (opts instanceof Object) {
    opts.host = client.host;
    opts.port = client.port;

    if (! opts.headers)
      opts.headers = {};

    if (client.user)
      opts.headers[Headers.USER] = client.user;

    opts.headers[Headers.USER_AGENT] = this.userAgent;

    if (opts.body)
      contentBody = opts.body;
  }

  var req = http.request(opts, function(res){
    var response_code = res.statusCode;
    var response_data = '';
    res.setEncoding('utf8');
    res.on('data', function(chunk){
      response_data += chunk;
    });
    res.on('end', function(){
      var data = response_data;
      if (response_code < 300 && (data[0] === '{' || data[0] === '[')) {
        try { data = JSON.parse(data); }
        catch (x) {
          /* ignore json parse error (and don't parse) for non-json content body */
        }
      }
      callback(null, response_code, data);
    });
  });

  req.on('error', function(e){
    callback(e);
  });

  if (contentBody)
    req.write(contentBody);

  req.end();
};

Client.prototype.nodes = function(opts, callback) { // TODO: "failed" nodes not supported yet
  if (! callback) {
    callback = opts;
    opts = {};
  }

  this.request({ method: 'GET', path: '/v1/node' }, function(error, code, data){
    if (error || code !== 200) {
      var message = "node list api returns error" + (data && data.length > 0 ? ":" + data : "");
      callback({message: message, error: error, code: code});
      return;
    }
    callback(null, data);
  });
};

Client.prototype.query = function(query_id, callback) {
  this.request({ method: 'GET', path: '/v1/query/' + query_id }, function(error, code, data){
    if (error || code !== 200) {
      var message = "query info api returns error" + (data && data.length > 0 ? ":" + data : "");
      callback({message: message, error: error, code: code});
      return;
    }
    callback(null, data);
  });
};

Client.prototype.kill = function(query_id, callback) {
  this.request({ method: 'DELETE', path: '/v1/query/' + query_id }, function(error, code, data){
    if (error || code !== 204) {
      var message = "query kill api returns error" + (data && data.length > 0 ? ":" + data : "");
      callback({message: message, error: error, code: code});
      return;
    }
    if (callback)
      callback(null);
  });
};

Client.prototype.execute = function(opts, callback) {
  if (callback)
    this.executeResource(opts, callback);
  else
    this.statementResource(opts);
};

Client.prototype.executeResource = function(opts, callback) {
  var client = this;

  var query = null;
  if (opts instanceof Object) {
    query = opts.query;
  } else { // opts is query string itself
    query = opts;
    opts = {};
  }

  if (!opts.catalog && !this.catalog)
    throw {message: "catalog not specified"};
  if (!opts.schema && !this.schema)
    throw {message: "schema not specified"};

  var header = {};
  header[Headers.CATALOG] = opts.catalog || this.catalog;
  header[Headers.SCHEMA] = opts.schema || this.schema;

  var req = { method: 'POST', path: '/v1/execute', headers: header, body: query };
  client.request(req, function(err, code, content){
    if (err || code !== 200) {
      var message = "execution error" + (content && content.length > 0 ? ":" + content : "");
      callback({message:message, error: err, code: code});
      return;
    }
    // content: {"columns":[{"name":"Schema","type":"varchar"}],"data":[["default"],["information_schema"],["sys"]]}
    callback(null, content.data, content.columns);
  });
};

Client.prototype.statementResource = function(opts) {
  var client = this;
  var columns = null;

  if (!opts.catalog && !this.catalog)
    throw {message: "catalog not specified"};
  if (!opts.schema && !this.schema)
    throw {message: "schema not specified"};
  if (!opts.success && !opts.callback)
    throw {message: "callback function 'success' (or 'callback') not specified"};

  var header = {};
  header[Headers.CATALOG] = opts.catalog || this.catalog;
  header[Headers.SCHEMA] = opts.schema || this.schema;

  var fetch_info = opts.info || false;

  var cancel_checker = opts.cancel;
  var state_callback = opts.state;
  var columns_callback = opts.columns;
  var data_callback = opts.data;
  var success_callback = opts.success || opts.callback;
  var error_callback = opts.error || opts.callback;

  var req = { method: 'POST', path: '/v1/statement', headers: header, body: opts.query };
  client.request(req, function(err, code, data){
    if (err || code !== 200 || (data && data.error)) {
      if (error_callback) {
        var message = "execution error" + (data && data.length > 0 ? ":" + data : "");
        if (data && data.error && data.error.message)
          message = data.error.message;
        error_callback({message:message, error: (err || data.error), code: code});
      }
      return;
    }
    /*
    var data = {
      "stats": {
        "processedBytes": 0, "processedRows": 0,
        "wallTimeMillis": 0, "cpuTimeMillis": 0, "userTimeMillis": 0,
        "state": "QUEUED",
        "scheduled": false,
        "nodes": 0,
        "totalSplits": 0, "queuedSplits": 0, "runningSplits": 0, "completedSplits": 0,
      },
      "nextUri": "http://localhost:8080/v1/statement/20140120_032523_00000_32v8g/1",
      "infoUri": "http://localhost:8080/v1/query/20140120_032523_00000_32v8g",
      "id": "20140120_032523_00000_32v8g"
    };
     */
    if (!data.id || !data.nextUri || !data.infoUri) {
      var error_message = null;
      if (!data.id)
        error_message = "query id missing in response for POST /v1/statement";
      else if (!data.nextUri)
        error_message = "nextUri missing in response for POST /v1/statement";
      else if (!data.infoUri)
        error_message = "infoUri missing in response for POST /v1/statement";
      error_callback({message: error_message, data: data});
      return;
    }
    var last_state = null;
    var firstNextUri = data.nextUri; // TODO: check the cases without nextUri for /statement ?
    var fetch_next = function(next_uri){
      /*
       * 1st time
{
 "stats": {
   "rootStage": {
     "subStages": [
       {
         "subStages": [],
         "processedBytes": 83103149, "processedRows": 2532704,
         "wallTimeMillis": 20502, "cpuTimeMillis": 3431, "userTimeMillis": 3210,
         "stageId": "1", "state": "FINISHED", "done": true,
         "nodes": 3,
         "totalSplits": 420, "queuedSplits": 0, "runningSplits": 0, "completedSplits": 420
       }
     ],
     // same as substage
   },
   // same as substage
   "state": "RUNNING",
 },
 "data": [ [ 1266352 ] ],
 "columns": [ { "type": "bigint", "name": "cnt" } ],
 "nextUri": "http://localhost:8080/v1/statement/20140120_032523_00000_32v8g/2",
 "partialCancelUri": "http://10.0.0.0:8080/v1/stage/20140120_032523_00000_32v8g.0",
 "infoUri": "http://localhost:8080/v1/query/20140120_032523_00000_32v8g",
 "id": "20140120_032523_00000_32v8g"
}
       */
      /*
       * 2nd time
{
 "stats": {
   // ....
   "state": "FINISHED",
 },
 "columns": [ { "type": "bigint", "name": "cnt" } ],
 "infoUri": "http://localhost:8080/v1/query/20140120_032523_00000_32v8g",
 "id": "20140120_032523_00000_32v8g"
}
       */
      if (cancel_checker && cancel_checker()) {
        client.request({ method: 'DELETE', path: next_uri }, function(error, code, data){
          if (error || code !== 204) {
            error_callback({message: "query fetch canceled, but Presto query cancel may fail", error: error, code: code});
          } else {
            error_callback({message: "query fetch canceled by operation"});
          }
        });
        return;
      }
      client.request(next_uri, function(error, code, response){
        if (response.error) {
          error_callback(response.error);
          return;
        }

        if (state_callback && last_state !== response.stats.state) {
          state_callback(null, response.id, response.stats);
          last_state = response.stats.state;
        }

        if (columns_callback && response.columns && !columns) {
          columns = response.columns;
          columns_callback(null, columns);
        }

        /* presto-main/src/main/java/com/facebook/presto/execution/QueryState.java
         * QUEUED, PLANNING, STARTING, RUNNING, FINISHED, CANCELED, FAILED
         */
        if (response.stats.state === 'QUEUED'
          || response.stats.state === 'PLANNING'
          || response.stats.state === 'STARTING'
          || response.stats.state === 'RUNNING' && !response.data) {
          var next = response.nextUri;
          setTimeout(function(){ fetch_next(next); }, client.checkInterval);
          return;
        }

        if (data_callback && response.data) {
          data_callback(null, response.data, response.columns, response.stats);
        }

        if (response.nextUri) {
          var next = response.nextUri;
          setTimeout(function(){ fetch_next(next); }, client.checkInterval);
          return;
        }

        var finishedStats = response.stats;

        if (fetch_info && response.infoUri) {
          client.request(response.infoUri, function(error, code, response){
            success_callback(null, finishedStats, response);
          });
        }
        else {
          success_callback(null, finishedStats);
        }
      });
    };
    fetch_next(firstNextUri);
  });
};
<|endoftext|>