<commit_before>const Deferred = require("promised-io/promise").Deferred;
const all = require('promised-io/promise').all;
const when = require('promised-io/promise').when;
const debug = require('debug')('p3api-server:TranscriptomicsGene');
const request = require('request');
const config = require("../config");
const distributeURL = config.get("distributeURL");
const workspaceAPI = config.get("workspaceAPI");

function getWorkspaceObjects(paths, metadataOnly, token){
	const def = new Deferred();

	if(!(paths instanceof Array)){
		paths = [paths];
	}
	paths = paths.map(decodeURIComponent);

	request({
		method: "POST",
		url: workspaceAPI,
		json: true,
		body: {
			id: 1,
			method: "Workspace.get",
			version: "1.1",
			params: [{objects: paths, metadata_only: metadataOnly}]
		},
		headers: {
			"accept": "application/json",
			"content-type": "application/json",
			"authorization": token
		}
	}, function(err, resObj, results){
		if(err){
			debug("Error retrieving object from workspace: ", err);

			def.reject(err);
			return;
		}
		if(results.result){

			// const data = [];

			const defs = results.result[0].map(function(obj){
				// debug("reading obj0: ", obj[0]);
				// debug("reading obj1: ", obj[1]);
				const meta = {
					name: obj[0][0],
					type: obj[0][1],
					path: obj[0][2],
					creation_time: obj[0][3],
					id: obj[0][4],
					owner_id: obj[0][5],
					size: obj[0][6],
					userMeta: obj[0][7],
					autoMeta: obj[0][8],
					user_permissions: obj[0][9],
					global_permission: obj[0][10],
					link_reference: obj[0][11]
				};
				// debug("meta: ", meta);
				if(metadataOnly){
					// data.push(meta);
					// return true;
					return meta;
					// def.resolve(meta);
					// return true;
				}
				if(!meta.link_reference){
					// data.push({metadata: meta, data: obj[1]});
					// return true;
					return ({metadata: meta, data: obj[1]});
					// def.resolve({metadata: meta, data: obj[1]});
					// return true;
				}else{

					const headers = {
						"Authorization": "OAuth " + token
					};

					// debug("headers: ", headers);
					const defShock = new Deferred();
					request({
						method: 'GET',
						url: meta.link_reference + "?download",
						headers: headers
					}, function(err, res, d){
						// debug("err", err);
						// debug("res: ", res);
						// debug("downloaded: ", d);
						defShock.resolve({
							metadata: meta,
							data: d
						});
						// return true;
					});
					return defShock.promise;
				}
			});
			// debug("defs: ", defs);
			all(defs).then(function(d){
				// debug("all defs are resolved", paths);
				// debug("body: ", d);
				def.resolve(d);
			});
		}
	});
	return def.promise;
}

function readWorkspaceExperiments(tgState, options){

	const wsExpIds = tgState['wsExpIds'];
	const wsComparisonIds = tgState['wsComparisonIds'];
	const def = new Deferred();

	when(getWorkspaceObjects(wsExpIds, true, options.token), function(data){

		// debug("getWorkspaceObjects:", data);

		const fileList = data.map(function(obj){
			const files = {};
			obj.autoMeta.output_files.forEach(function(arr){
				// debug(typeof arr, arr);
				const d = (arr instanceof Array) ? arr[0] : arr;
				const file = d.substr(d.lastIndexOf('/') + 1);
				const type = file.split('.')[0];
				files[type] = d;
			});

			return {meta: obj, files: files};
		});

		const expressionFiles = fileList.map(function(obj){
			return obj.files.expression;
		});

		// debug("expressionFiles: ", expressionFiles);

		return when(getWorkspaceObjects(expressionFiles, false, options.token), function(results){

			const p3FeatureIdSet = {};
			const p2FeatureIdSet = {};

			const expressions = results.map(function(d){

				// debug("expressionFile results", typeof d.data, wsComparisonIds);
				// debug(d.data);

				if(!wsComparisonIds){
					return JSON.parse(d.data)['expression'];
				}else{
					return JSON.parse(d.data)['expression'].filter(function(e){
						return wsComparisonIds.indexOf(e.pid) >= 0;
					});
				}
			});

			const flattened = [].concat.apply([], expressions);

			// debug("expressions: ", typeof flattened, flattened.length);

			flattened.forEach(function(expression){
				// debug("expression: ", typeof expression, expression.feature_id, expression.na_feature_id);
				if(expression.hasOwnProperty('feature_id')){
					if(!p3FeatureIdSet.hasOwnProperty(expression.feature_id)){
						p3FeatureIdSet[expression.feature_id] = true;
					}
				}else if(expression.hasOwnProperty('na_feature_id')){
					if(!p2FeatureIdSet.hasOwnProperty(expression.na_feature_id)){
						p2FeatureIdSet[expression.na_feature_id] = true;
					}
				}
			});

			// debug("expressions:", flattened.length, "p3 ids: ", Object.keys(p3FeatureIdSet).length, "p2 ids: ", Object.keys(p2FeatureIdSet).length);

			def.resolve({
				expressions: flattened,
				p3FeatureIds: Object.keys(p3FeatureIdSet),
				p2FeatureIds: Object.keys(p2FeatureIdSet)
			});
		});
	});

	return def.promise;
}

function readPublicExperiments(tgState, options){

	const def = new Deferred();

	request.post({
		url: distributeURL + 'transcriptomics_gene/',
		json: true,
		headers: {
			'Accept': "application/solr+json",
			'Content-Type': "application/rqlquery+x-www-form-urlencoded",
			'Authorization': options.token || ""
		},
		body: tgState.query + "&select(pid,refseq_locus_tag,feature_id,log_ratio,z_score)&limit(1)"
	}, function(error, res, response){

		if(error || res.statusCode !== 200){
			debug(error, res.statusCode)
		}

		const numFound = response.response.numFound;

		const fetchSize = 25000;
		const steps = Math.ceil(numFound / fetchSize);
		const allRequests = [];

		for(let i = 0; i < steps; i++){
			const deferred = new Deferred();
			const range = "items=" + (i * fetchSize) + '-' + ((i + 1) * fetchSize - 1);
			// debug("Range: ", range);

			request.post({
				url: distributeURL + 'transcriptomics_gene/',
				json: true,
				headers: {
					'Accept': "application/json",
					'Content-Type': "application/rqlquery+x-www-form-urlencoded",
					'Range': range,
					'Authorization': options.token || ""
				},
				body: tgState.query + "&select(pid,refseq_locus_tag,feature_id,log_ratio,z_score)"
			}, function(err, res, body){
				deferred.resolve(body);
			});
			allRequests.push(deferred);
		}

		all(allRequests).then(function(results){

			const expressions = [];
			const p3FeatureIdSet = {};
			// const p2FeatureIdSet = {};

			results.forEach(function(genes){

				// debug("genes count: ", genes.length);

				genes.forEach(function(gene){
					expressions.push(gene);

					if(gene.hasOwnProperty("feature_id")){
						if(!p3FeatureIdSet.hasOwnProperty(gene.feature_id)){
							p3FeatureIdSet[gene.feature_id] = true;
						}
						// }else if(gene.hasOwnProperty("na_feature_id")){
						// 	if(!p2FeatureIdSet.hasOwnProperty(gene.na_feature_id)){
						// 		p2FeatureIdSet[gene.na_feature_id] = true;
						// 	}
					}
				});
			});

			// debug("expressions:", expressions.length, "p3 ids: ", Object.keys(p3FeatureIdSet).length, "p2 ids: ", Object.keys(p2FeatureIdSet).length);

			def.resolve({expressions: expressions, p3FeatureIds: Object.keys(p3FeatureIdSet), p2FeatureIds: []});
		});
	});

	return def.promise;
}

function processTranscriptomicsGene(tgState, options){
	const def = new Deferred();

	let wsCall = new Deferred();
	if(tgState.hasOwnProperty('wsExpIds')){
		wsCall = readWorkspaceExperiments(tgState, options);
	}else{
		wsCall.resolve({expressions: [], p3FeatureIds: [], p2FeatureIds: []});
	}

	let publicCall = new Deferred();
	if(tgState.hasOwnProperty('pbExpIds')){
		publicCall = readPublicExperiments(tgState, options);
	}else{
		publicCall.resolve({expressions: [], p3FeatureIds: [], p2FeatureIds: []});
	}

	all(publicCall, wsCall).then(function(results){
		// all(publicCall).then(function(results){

		const comparisonIdList = tgState.comparisonIds;

		const wsP3FeatureIdList = results[1].p3FeatureIds;
		const wsP2FeatureIdList = results[1].p2FeatureIds;
		const wsExpressions = results[1].expressions;

		const pbP3FeatureIdList = results[0].p3FeatureIds;
		const pbP2FeatureIdList = results[0].p2FeatureIds; // []
		const pbExpressions = results[0].expressions;

		const p3FeatureIdList = wsP3FeatureIdList.concat(pbP3FeatureIdList);
		const p2FeatureIdList = wsP2FeatureIdList.concat(pbP2FeatureIdList);
		const expressions = wsExpressions.concat(pbExpressions);

		debug("p3 ids: ", p3FeatureIdList.length, "p2 ids: ", p2FeatureIdList.length);

		const query = {
			q: [(p3FeatureIdList.length > 0) ? 'feature_id:(' + p3FeatureIdList.join(' OR ') + ')' : '',
				(p3FeatureIdList.length > 0 && p2FeatureIdList.length > 0) ? ' OR ' : '',
				(p2FeatureIdList.length > 0) ? 'p2_feature_id:(' + p2FeatureIdList.join(' OR ') + ')' : ''].join(''),
			fl: 'feature_id,p2_feature_id,strand,product,accession,start,end,patric_id,alt_locus_tag,genome_name,gene'
		};
		// debug("genome_feature query: ", query);
		const q = Object.keys(query).map(p => p + "=" + query[p]).join("&");

		const fetchSize = 25000;
		const steps = Math.ceil((p3FeatureIdList.length + p2FeatureIdList.length) / fetchSize);
		const allRequests = [];

		for(let i = 0; i < steps; i++){
			const subDef = Deferred();
			const range = "items=" + (i * fetchSize) + '-' + ((i + 1) * fetchSize - 1);
			// debug("Range: ", range);

			request.post({
				url: distributeURL + 'genome_feature/',
				json: true,
				headers: {
					'Accept': "application/json",
					'Content-Type': "application/solrquery+x-www-form-urlencoded",
					'Range': range,
					'Authorization': options.token || ''
				},
				body: q
			}, function(err, res, body){
				subDef.resolve(body);
			});
			allRequests.push(subDef);
		}

		all(allRequests).then(function(body){

			const features = [].concat.apply([], body);

			const expressionHash = {};

			expressions.forEach(function(expression){
				let featureId;
				if(expression.hasOwnProperty("feature_id")){
					featureId = expression.feature_id;
				}else if(expression.hasOwnProperty("na_feature_id")){
					featureId = expression.na_feature_id;
				}

				if(!expressionHash.hasOwnProperty(featureId)){

					var expr = {samples: {}};
					(expression.hasOwnProperty('feature_id')) ? expr.feature_id = expression.feature_id : '';
					(expression.hasOwnProperty('na_feature_id')) ? expr.p2_feature_id = expression.na_feature_id : '';
					(expression.hasOwnProperty('refseq_locus_tag')) ? expr.refseq_locus_tag = expression.refseq_locus_tag : expression.exp_locus_tag;
					var log_ratio = expression.log_ratio, z_score = expression.z_score;
					expr.samples[expression.pid.toString()] = {
						log_ratio: log_ratio || '',
						z_score: z_score || ''
					};
					expr.up = (log_ratio != null && Number(log_ratio) > 0) ? 1 : 0;
					expr.down = (log_ratio != null && Number(log_ratio) < 0) ? 1 : 0;

					expressionHash[featureId] = expr;
				}else{
					expr = expressionHash[featureId];
					if(!expr.samples.hasOwnProperty(expression.pid.toString())){
						log_ratio = expression.log_ratio;
						z_score = expression.z_score;
						expr.samples[expression.pid.toString()] = {
							log_ratio: log_ratio || '',
							z_score: z_score || ''
						};
						(log_ratio != null && Number(log_ratio) > 0) ? expr.up++ : '';
						(log_ratio != null && Number(log_ratio) < 0) ? expr.down++ : '';

						expressionHash[featureId] = expr;
					}
				}
			});

			const data = [];
			features.forEach(function(feature){

				let expr;
				if(expressionHash.hasOwnProperty(feature.feature_id)){
					expr = expressionHash[feature.feature_id];
				}else if(expressionHash.hasOwnProperty(feature.p2_feature_id)){
					expr = expressionHash[feature.p2_feature_id];
				}

				// build expr object
				let count = 0;
				expr.sample_binary = comparisonIdList.map(function(comparisonId){
					if(expr.samples.hasOwnProperty(comparisonId) && expr.samples[comparisonId].log_ratio !== ''){
						count++;
						return "1";
					}else{
						return "0";
					}
				}).join('');
				expr.sample_size = count;

				const datum = Object.assign(feature, expr);
				data.push(datum);
			});

			def.resolve(data);
		});
	});

	return def.promise;
}

module.exports = {
	requireAuthentication: false,
	validate: function(params){
		const tgState = params[0];
		return tgState && tgState.comparisonIds.length > 0;
	},
	execute: function(params){
		const def = new Deferred();

		const tgState = params[0];
		const opts = params[1];

		when(processTranscriptomicsGene(tgState, opts), function(result){
			def.resolve(result);
		}, function(err){
			def.reject("Unable to process protein family queries. " + err);
		});

		return def.promise;
	}
};<commit_msg>fix bug: refseq_locus_tags are not displayed. resolve PATRIC3/patric3_webiste#1200<commit_after>const Deferred = require("promised-io/promise").Deferred;
const all = require('promised-io/promise').all;
const when = require('promised-io/promise').when;
const debug = require('debug')('p3api-server:TranscriptomicsGene');
const request = require('request');
const config = require("../config");
const distributeURL = config.get("distributeURL");
const workspaceAPI = config.get("workspaceAPI");

function getWorkspaceObjects(paths, metadataOnly, token){
	const def = new Deferred();

	if(!(paths instanceof Array)){
		paths = [paths];
	}
	paths = paths.map(decodeURIComponent);

	request({
		method: "POST",
		url: workspaceAPI,
		json: true,
		body: {
			id: 1,
			method: "Workspace.get",
			version: "1.1",
			params: [{objects: paths, metadata_only: metadataOnly}]
		},
		headers: {
			"accept": "application/json",
			"content-type": "application/json",
			"authorization": token
		}
	}, function(err, resObj, results){
		if(err){
			debug("Error retrieving object from workspace: ", err);

			def.reject(err);
			return;
		}
		if(results.result){

			// const data = [];

			const defs = results.result[0].map(function(obj){
				// debug("reading obj0: ", obj[0]);
				// debug("reading obj1: ", obj[1]);
				const meta = {
					name: obj[0][0],
					type: obj[0][1],
					path: obj[0][2],
					creation_time: obj[0][3],
					id: obj[0][4],
					owner_id: obj[0][5],
					size: obj[0][6],
					userMeta: obj[0][7],
					autoMeta: obj[0][8],
					user_permissions: obj[0][9],
					global_permission: obj[0][10],
					link_reference: obj[0][11]
				};
				// debug("meta: ", meta);
				if(metadataOnly){
					// data.push(meta);
					// return true;
					return meta;
					// def.resolve(meta);
					// return true;
				}
				if(!meta.link_reference){
					// data.push({metadata: meta, data: obj[1]});
					// return true;
					return ({metadata: meta, data: obj[1]});
					// def.resolve({metadata: meta, data: obj[1]});
					// return true;
				}else{

					const headers = {
						"Authorization": "OAuth " + token
					};

					// debug("headers: ", headers);
					const defShock = new Deferred();
					request({
						method: 'GET',
						url: meta.link_reference + "?download",
						headers: headers
					}, function(err, res, d){
						// debug("err", err);
						// debug("res: ", res);
						// debug("downloaded: ", d);
						defShock.resolve({
							metadata: meta,
							data: d
						});
						// return true;
					});
					return defShock.promise;
				}
			});
			// debug("defs: ", defs);
			all(defs).then(function(d){
				// debug("all defs are resolved", paths);
				// debug("body: ", d);
				def.resolve(d);
			});
		}
	});
	return def.promise;
}

function readWorkspaceExperiments(tgState, options){

	const wsExpIds = tgState['wsExpIds'];
	const wsComparisonIds = tgState['wsComparisonIds'];
	const def = new Deferred();

	when(getWorkspaceObjects(wsExpIds, true, options.token), function(data){

		// debug("getWorkspaceObjects:", data);

		const fileList = data.map(function(obj){
			const files = {};
			obj.autoMeta.output_files.forEach(function(arr){
				// debug(typeof arr, arr);
				const d = (arr instanceof Array) ? arr[0] : arr;
				const file = d.substr(d.lastIndexOf('/') + 1);
				const type = file.split('.')[0];
				files[type] = d;
			});

			return {meta: obj, files: files};
		});

		const expressionFiles = fileList.map(function(obj){
			return obj.files.expression;
		});

		// debug("expressionFiles: ", expressionFiles);

		return when(getWorkspaceObjects(expressionFiles, false, options.token), function(results){

			const p3FeatureIdSet = {};
			const p2FeatureIdSet = {};

			const expressions = results.map(function(d){

				// debug("expressionFile results", typeof d.data, wsComparisonIds);
				// debug(d.data);

				if(!wsComparisonIds){
					return JSON.parse(d.data)['expression'];
				}else{
					return JSON.parse(d.data)['expression'].filter(function(e){
						return wsComparisonIds.indexOf(e.pid) >= 0;
					});
				}
			});

			const flattened = [].concat.apply([], expressions);

			// debug("expressions: ", typeof flattened, flattened.length);

			flattened.forEach(function(expression){
				// debug("expression: ", typeof expression, expression.feature_id, expression.na_feature_id);
				if(expression.hasOwnProperty('feature_id')){
					if(!p3FeatureIdSet.hasOwnProperty(expression.feature_id)){
						p3FeatureIdSet[expression.feature_id] = true;
					}
				}else if(expression.hasOwnProperty('na_feature_id')){
					if(!p2FeatureIdSet.hasOwnProperty(expression.na_feature_id)){
						p2FeatureIdSet[expression.na_feature_id] = true;
					}
				}
			});

			// debug("expressions:", flattened.length, "p3 ids: ", Object.keys(p3FeatureIdSet).length, "p2 ids: ", Object.keys(p2FeatureIdSet).length);

			def.resolve({
				expressions: flattened,
				p3FeatureIds: Object.keys(p3FeatureIdSet),
				p2FeatureIds: Object.keys(p2FeatureIdSet)
			});
		});
	});

	return def.promise;
}

function readPublicExperiments(tgState, options){

	const def = new Deferred();

	request.post({
		url: distributeURL + 'transcriptomics_gene/',
		json: true,
		headers: {
			'Accept': "application/solr+json",
			'Content-Type': "application/rqlquery+x-www-form-urlencoded",
			'Authorization': options.token || ""
		},
		body: tgState.query + "&select(pid,refseq_locus_tag,feature_id,log_ratio,z_score)&limit(1)"
	}, function(error, res, response){

		if(error || res.statusCode !== 200){
			debug(error, res.statusCode)
		}

		const numFound = response.response.numFound;

		const fetchSize = 25000;
		const steps = Math.ceil(numFound / fetchSize);
		const allRequests = [];

		for(let i = 0; i < steps; i++){
			const deferred = new Deferred();
			const range = "items=" + (i * fetchSize) + '-' + ((i + 1) * fetchSize - 1);
			// debug("Range: ", range);

			request.post({
				url: distributeURL + 'transcriptomics_gene/',
				json: true,
				headers: {
					'Accept': "application/json",
					'Content-Type': "application/rqlquery+x-www-form-urlencoded",
					'Range': range,
					'Authorization': options.token || ""
				},
				body: tgState.query + "&select(pid,refseq_locus_tag,feature_id,log_ratio,z_score)"
			}, function(err, res, body){
				deferred.resolve(body);
			});
			allRequests.push(deferred);
		}

		all(allRequests).then(function(results){

			const expressions = [];
			const p3FeatureIdSet = {};
			// const p2FeatureIdSet = {};

			results.forEach(function(genes){

				// debug("genes count: ", genes.length);

				genes.forEach(function(gene){
					expressions.push(gene);

					if(gene.hasOwnProperty("feature_id")){
						if(!p3FeatureIdSet.hasOwnProperty(gene.feature_id)){
							p3FeatureIdSet[gene.feature_id] = true;
						}
						// }else if(gene.hasOwnProperty("na_feature_id")){
						// 	if(!p2FeatureIdSet.hasOwnProperty(gene.na_feature_id)){
						// 		p2FeatureIdSet[gene.na_feature_id] = true;
						// 	}
					}
				});
			});

			// debug("expressions:", expressions.length, "p3 ids: ", Object.keys(p3FeatureIdSet).length, "p2 ids: ", Object.keys(p2FeatureIdSet).length);

			def.resolve({expressions: expressions, p3FeatureIds: Object.keys(p3FeatureIdSet), p2FeatureIds: []});
		});
	});

	return def.promise;
}

function processTranscriptomicsGene(tgState, options){
	const def = new Deferred();

	let wsCall = new Deferred();
	if(tgState.hasOwnProperty('wsExpIds')){
		wsCall = readWorkspaceExperiments(tgState, options);
	}else{
		wsCall.resolve({expressions: [], p3FeatureIds: [], p2FeatureIds: []});
	}

	let publicCall = new Deferred();
	if(tgState.hasOwnProperty('pbExpIds')){
		publicCall = readPublicExperiments(tgState, options);
	}else{
		publicCall.resolve({expressions: [], p3FeatureIds: [], p2FeatureIds: []});
	}

	all(publicCall, wsCall).then(function(results){
		// all(publicCall).then(function(results){

		const comparisonIdList = tgState.comparisonIds;

		const wsP3FeatureIdList = results[1].p3FeatureIds;
		const wsP2FeatureIdList = results[1].p2FeatureIds;
		const wsExpressions = results[1].expressions;

		const pbP3FeatureIdList = results[0].p3FeatureIds;
		const pbP2FeatureIdList = results[0].p2FeatureIds; // []
		const pbExpressions = results[0].expressions;

		const p3FeatureIdList = wsP3FeatureIdList.concat(pbP3FeatureIdList);
		const p2FeatureIdList = wsP2FeatureIdList.concat(pbP2FeatureIdList);
		const expressions = wsExpressions.concat(pbExpressions);

		debug("p3 ids: ", p3FeatureIdList.length, "p2 ids: ", p2FeatureIdList.length);

		const query = {
			q: [(p3FeatureIdList.length > 0) ? 'feature_id:(' + p3FeatureIdList.join(' OR ') + ')' : '',
				(p3FeatureIdList.length > 0 && p2FeatureIdList.length > 0) ? ' OR ' : '',
				(p2FeatureIdList.length > 0) ? 'p2_feature_id:(' + p2FeatureIdList.join(' OR ') + ')' : ''].join(''),
			fl: 'feature_id,p2_feature_id,strand,product,accession,start,end,patric_id,alt_locus_tag,genome_name,gene'
		};
		// debug("genome_feature query: ", query);
		const q = Object.keys(query).map(p => p + "=" + query[p]).join("&");

		const fetchSize = 25000;
		const steps = Math.ceil((p3FeatureIdList.length + p2FeatureIdList.length) / fetchSize);
		const allRequests = [];

		for(let i = 0; i < steps; i++){
			const subDef = Deferred();
			const range = "items=" + (i * fetchSize) + '-' + ((i + 1) * fetchSize - 1);
			// debug("Range: ", range);

			request.post({
				url: distributeURL + 'genome_feature/',
				json: true,
				headers: {
					'Accept': "application/json",
					'Content-Type': "application/solrquery+x-www-form-urlencoded",
					'Range': range,
					'Authorization': options.token || ''
				},
				body: q
			}, function(err, res, body){
				subDef.resolve(body);
			});
			allRequests.push(subDef);
		}

		all(allRequests).then(function(body){

			const features = [].concat.apply([], body);

			const expressionHash = {};

			expressions.forEach(function(expression){
				let featureId;
				if(expression.hasOwnProperty("feature_id")){
					featureId = expression.feature_id;
				}else if(expression.hasOwnProperty("na_feature_id")){
					featureId = expression.na_feature_id;
				}

				if(!expressionHash.hasOwnProperty(featureId)){

					var expr = {samples: {}};
					(expression.hasOwnProperty('feature_id')) ? expr.feature_id = expression.feature_id : '';
					(expression.hasOwnProperty('na_feature_id')) ? expr.p2_feature_id = expression.na_feature_id : '';
					(expression.hasOwnProperty('refseq_locus_tag')) ? expr.refseq_locus_tag = expression.refseq_locus_tag : expr.refseq_locus_tag = expression.exp_locus_tag;
					var log_ratio = expression.log_ratio, z_score = expression.z_score;
					expr.samples[expression.pid.toString()] = {
						log_ratio: log_ratio || '',
						z_score: z_score || ''
					};
					expr.up = (log_ratio != null && Number(log_ratio) > 0) ? 1 : 0;
					expr.down = (log_ratio != null && Number(log_ratio) < 0) ? 1 : 0;

					expressionHash[featureId] = expr;
				}else{
					expr = expressionHash[featureId];
					if(!expr.samples.hasOwnProperty(expression.pid.toString())){
						log_ratio = expression.log_ratio;
						z_score = expression.z_score;
						expr.samples[expression.pid.toString()] = {
							log_ratio: log_ratio || '',
							z_score: z_score || ''
						};
						(log_ratio != null && Number(log_ratio) > 0) ? expr.up++ : '';
						(log_ratio != null && Number(log_ratio) < 0) ? expr.down++ : '';

						expressionHash[featureId] = expr;
					}
				}
			});

			const data = [];
			features.forEach(function(feature){

				let expr;
				if(expressionHash.hasOwnProperty(feature.feature_id)){
					expr = expressionHash[feature.feature_id];
				}else if(expressionHash.hasOwnProperty(feature.p2_feature_id)){
					expr = expressionHash[feature.p2_feature_id];
				}

				// build expr object
				let count = 0;
				expr.sample_binary = comparisonIdList.map(function(comparisonId){
					if(expr.samples.hasOwnProperty(comparisonId) && expr.samples[comparisonId].log_ratio !== ''){
						count++;
						return "1";
					}else{
						return "0";
					}
				}).join('');
				expr.sample_size = count;

				const datum = Object.assign(feature, expr);
				data.push(datum);
			});

			def.resolve(data);
		});
	});

	return def.promise;
}

module.exports = {
	requireAuthentication: false,
	validate: function(params){
		const tgState = params[0];
		return tgState && tgState.comparisonIds.length > 0;
	},
	execute: function(params){
		const def = new Deferred();

		const tgState = params[0];
		const opts = params[1];

		when(processTranscriptomicsGene(tgState, opts), function(result){
			def.resolve(result);
		}, function(err){
			def.reject("Unable to process protein family queries. " + err);
		});

		return def.promise;
	}
};<|endoftext|>


<commit_before>r.liveupdate = {
    _pixelInterval: 10 * 60 * 1000,
    _favicon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAA/1BMVEUpLzY+PDpGSk5HR0dKTlNSW2VTXGVVXmdWWl5bYWZdZGtfanVfbHpgXVtianNjbXhkZWdkam1lcX1nc4BpdYJqa2xscHZucHJueYRvfoxwfIhxcG93e4B3h5h4iJh5ipt7gIB8ipl+fn6BgYGImKOJh4aKjY2Mna6NobSOn7CQo7iar8Wfnp2juM6lo6Gmuc6mu8moqaqsw9etwcmurq6vrauxyN2xyN+0s7K+u7m/1u7C2vPF3fbI4PrJ4fvJ4/7Ly8vPzMnP6P7S0tLT0c/W8P/Z19Xd9/7d+P7e3Nrw8PDz9vT69/T+EA/+MjD+pqT+srD+w8H+zsz+/v7///9fla50AAAAuElEQVR42l2P2RaBUBhGT0WZ54jIPM8h0zE7SBL97/8uYoXFvtwXe30fIn8ggn94i8XMGSkFQvmPwFXvwOe/OGyx3OJYF+OUq26LcS2LMs05Xj0bPY+QDFc6k1bOnRQ8PYLiKlXW4IlWptQ4QWlxCIZ+h7tuwFBME9RgAG5XE8zrDYBpWNEEfElY0RO7Aejvzrs+wIa1xFGmp7AuFoprmNLya4fC8aP9YT/iOeX9pS1Fg1GpbZ/74wFo2jf64C4agwAAAABJRU5ErkJggg==',

    init: function () {
        this.$listing = $('.liveupdate-listing')
        this.$table = this.$listing.find('table tbody')
        this.$statusField = this.$listing.find('tr.initial td')

        this.$listing.find('nav.nextprev').remove()
        $(window)
            .scroll($.proxy(this, '_loadMoreIfNearBottom'))
            .scroll()  // in case of a short page / tall window

        if (r.config.liveupdate_websocket) {
            this._websocket = new r.WebSocket(r.config.liveupdate_websocket)
            this._websocket.on({
                'connecting': this._onWebSocketConnecting,
                'connected': this._onWebSocketConnected,
                'disconnected': this._onWebSocketDisconnected,
                'reconnecting': this._onWebSocketReconnecting,
                'message:delete': this._onDelete,
                'message:strike': this._onStrike,
                'message:activity': this._onActivityUpdated,
                'message:refresh': this._onRefresh,
                'message:settings': this._onSettingsChanged,
                'message:update': this._onNewUpdate
            }, this)
        }

        Tinycon.setOptions({
            'background': '#ff4500'
        })
        Tinycon.setImage(this._favicon)

        $(document).on({
            'show': $.proxy(this, '_onPageVisible'),
            'hide': $.proxy(this, '_onPageHide')
        })
        this._onPageVisible()

        this._fetchPixel()
    },

    _onPageVisible: function () {
        if (this._needToFetchPixel) {
            this._fetchPixel()
        }

        this._pageVisible = true
        this._unreadUpdates = 0
        this._needToFetchPixel = false
        Tinycon.setBubble()
    },

    _onPageHide: function () {
        this._pageVisible = false
    },

    _onWebSocketConnecting: function () {
        this.$statusField.addClass('connecting')
                         .text(r._('connecting to update server...'))
    },

    _onWebSocketConnected: function () {
        this.$statusField.removeClass('connecting')
                         .text(r._('updating in real time...'))
    },

    _onWebSocketDisconnected: function () {
        this.$statusField.removeClass('connecting')
                         .addClass('error')
                         .text(r._('could not connect to update servers. please refresh.'))
    },

    _onWebSocketReconnecting: function (delay) {
        this.$statusField.removeClass('connecting')
                         .text(r._('lost connection to update server. one moment...'))
    },

    _onRefresh: function () {
        // delay a random amount to reduce thundering herd
        var delay = Math.random() * 300 * 1000
        setTimeout(function () { location.reload() }, delay)
    },

    _onNewUpdate: function (data) {
        var $initial = this.$listing.find('tr.initial')

        // this must've been the first update. refresh to get a proper listing.
        if (!this.$listing.length)
            window.location.reload()

        _.each(data, function (thing) {
            var $newThing = $($.unsafe(thing.data.content))
            if (r.liveupdate.editor) {
                r.liveupdate.editor._addButtons($newThing.find('td'))
            }
            $initial.after($newThing)
        })

        if (!this._pageVisible) {
            this._unreadUpdates += data.length
            Tinycon.setBubble(this._unreadUpdates)
        }
    },

    _onDelete: function (id) {
        $.things(id).remove()
    },

    _onStrike: function (id) {
        $.things(id).addClass('stricken')
    },

    _onActivityUpdated: function (visitors) {
        var text = visitors.count
        if (visitors.fuzzed)
            text = '~' + text

        // TODO: animate this?
        $('#visitor-count .count').text(text)
    },

    _onSettingsChanged: function (changes) {
        if ('title' in changes) {
            $('#liveupdate-title').text(changes['title'])
            $('#header .pagename a').text(changes['title'])
            document.title = r._('[live]') + ' ' + changes['title']
        }

        if ('description' in changes) {
            $('.sidebar .md').html($.unsafe(changes['description']))
        }
    },

    _loadMoreIfNearBottom: function () {
        var hasUpdates = (this.$listing.length != 0)
        var isLoading = this.$listing.hasClass('loading')
        var canLoadMore = (this.$table.find('.final').length == 0)

        if (!hasUpdates || isLoading || !canLoadMore)
            return

        // technically, window.innerHeight includes the horizontal
        // scrollbar if present. oh well.
        var bottomOfTable = this.$table.offset().top + this.$table.height()
        var topOfLastScreenful = bottomOfTable - window.innerHeight
        var nearBottom = ($(window).scrollTop() + 250 >= topOfLastScreenful)

        if (nearBottom)
            this._loadMore()
    },

    _loadMore: function () {
        var lastId = this.$table.find('tr:last-child').data('fullname')

        // in case we get stuck in a loop somehow, bail out.
        if (lastId == this.lastFetchedId)
            return

        var params = $.param({
                'bare': 'y',
                'after': lastId,
                'count': this.$table.find('tr.thing').length
            })
        var url = '/live/' + r.config.liveupdate_event + '/?' + params

        this.$listing.addClass('loading')

        $.ajax({
            'url': url,
            'dataType': 'html'
        })
            .done($.proxy(function (response) {
                var $fragment = $(response),
                    $newRows = $fragment.find('.liveupdate-listing tbody').children()

                this.$listing.trigger('more-updates', [$newRows])
                this.$table.append($newRows)
                this.lastFetchedId = lastId

                r.timetext.refresh()
            }, this))
            .always($.proxy(function () {
                this.$listing.removeClass('loading')
            }, this))
    },

    _fetchPixel: function () {
        if (!this._pageVisible) {
            this._needToFetchPixel = true
            return
        }

        var pixel = new Image()
        pixel.src = '//' + r.config.liveupdate_pixel_domain +
                    '/live/' + r.config.liveupdate_event + '/pixel.png' +
                    '?rand=' + Math.random()

        var delay = Math.floor(this._pixelInterval -
                               this._pixelInterval * Math.random() / 2)
        setTimeout($.proxy(this, '_fetchPixel'), delay)
    }
}

r.liveupdate.init()
<commit_msg>Apply timetext to websocket'd updates before display.<commit_after>r.liveupdate = {
    _pixelInterval: 10 * 60 * 1000,
    _favicon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAA/1BMVEUpLzY+PDpGSk5HR0dKTlNSW2VTXGVVXmdWWl5bYWZdZGtfanVfbHpgXVtianNjbXhkZWdkam1lcX1nc4BpdYJqa2xscHZucHJueYRvfoxwfIhxcG93e4B3h5h4iJh5ipt7gIB8ipl+fn6BgYGImKOJh4aKjY2Mna6NobSOn7CQo7iar8Wfnp2juM6lo6Gmuc6mu8moqaqsw9etwcmurq6vrauxyN2xyN+0s7K+u7m/1u7C2vPF3fbI4PrJ4fvJ4/7Ly8vPzMnP6P7S0tLT0c/W8P/Z19Xd9/7d+P7e3Nrw8PDz9vT69/T+EA/+MjD+pqT+srD+w8H+zsz+/v7///9fla50AAAAuElEQVR42l2P2RaBUBhGT0WZ54jIPM8h0zE7SBL97/8uYoXFvtwXe30fIn8ggn94i8XMGSkFQvmPwFXvwOe/OGyx3OJYF+OUq26LcS2LMs05Xj0bPY+QDFc6k1bOnRQ8PYLiKlXW4IlWptQ4QWlxCIZ+h7tuwFBME9RgAG5XE8zrDYBpWNEEfElY0RO7Aejvzrs+wIa1xFGmp7AuFoprmNLya4fC8aP9YT/iOeX9pS1Fg1GpbZ/74wFo2jf64C4agwAAAABJRU5ErkJggg==',

    init: function () {
        this.$listing = $('.liveupdate-listing')
        this.$table = this.$listing.find('table tbody')
        this.$statusField = this.$listing.find('tr.initial td')

        this.$listing.find('nav.nextprev').remove()
        $(window)
            .scroll($.proxy(this, '_loadMoreIfNearBottom'))
            .scroll()  // in case of a short page / tall window

        if (r.config.liveupdate_websocket) {
            this._websocket = new r.WebSocket(r.config.liveupdate_websocket)
            this._websocket.on({
                'connecting': this._onWebSocketConnecting,
                'connected': this._onWebSocketConnected,
                'disconnected': this._onWebSocketDisconnected,
                'reconnecting': this._onWebSocketReconnecting,
                'message:delete': this._onDelete,
                'message:strike': this._onStrike,
                'message:activity': this._onActivityUpdated,
                'message:refresh': this._onRefresh,
                'message:settings': this._onSettingsChanged,
                'message:update': this._onNewUpdate
            }, this)
        }

        Tinycon.setOptions({
            'background': '#ff4500'
        })
        Tinycon.setImage(this._favicon)

        $(document).on({
            'show': $.proxy(this, '_onPageVisible'),
            'hide': $.proxy(this, '_onPageHide')
        })
        this._onPageVisible()

        this._fetchPixel()
    },

    _onPageVisible: function () {
        if (this._needToFetchPixel) {
            this._fetchPixel()
        }

        this._pageVisible = true
        this._unreadUpdates = 0
        this._needToFetchPixel = false
        Tinycon.setBubble()
    },

    _onPageHide: function () {
        this._pageVisible = false
    },

    _onWebSocketConnecting: function () {
        this.$statusField.addClass('connecting')
                         .text(r._('connecting to update server...'))
    },

    _onWebSocketConnected: function () {
        this.$statusField.removeClass('connecting')
                         .text(r._('updating in real time...'))
    },

    _onWebSocketDisconnected: function () {
        this.$statusField.removeClass('connecting')
                         .addClass('error')
                         .text(r._('could not connect to update servers. please refresh.'))
    },

    _onWebSocketReconnecting: function (delay) {
        this.$statusField.removeClass('connecting')
                         .text(r._('lost connection to update server. one moment...'))
    },

    _onRefresh: function () {
        // delay a random amount to reduce thundering herd
        var delay = Math.random() * 300 * 1000
        setTimeout(function () { location.reload() }, delay)
    },

    _onNewUpdate: function (data) {
        var $initial = this.$listing.find('tr.initial')

        // this must've been the first update. refresh to get a proper listing.
        if (!this.$listing.length)
            window.location.reload()

        var now = Date.now()
        _.each(data, function (thing) {
            var $newThing = $($.unsafe(thing.data.content))
            if (r.liveupdate.editor) {
                r.liveupdate.editor._addButtons($newThing.find('td'))
            }
            $initial.after($newThing)
            r.timetext.refreshOne($newThing.find('time.live'), now)
        })

        if (!this._pageVisible) {
            this._unreadUpdates += data.length
            Tinycon.setBubble(this._unreadUpdates)
        }
    },

    _onDelete: function (id) {
        $.things(id).remove()
    },

    _onStrike: function (id) {
        $.things(id).addClass('stricken')
    },

    _onActivityUpdated: function (visitors) {
        var text = visitors.count
        if (visitors.fuzzed)
            text = '~' + text

        // TODO: animate this?
        $('#visitor-count .count').text(text)
    },

    _onSettingsChanged: function (changes) {
        if ('title' in changes) {
            $('#liveupdate-title').text(changes['title'])
            $('#header .pagename a').text(changes['title'])
            document.title = r._('[live]') + ' ' + changes['title']
        }

        if ('description' in changes) {
            $('.sidebar .md').html($.unsafe(changes['description']))
        }
    },

    _loadMoreIfNearBottom: function () {
        var hasUpdates = (this.$listing.length != 0)
        var isLoading = this.$listing.hasClass('loading')
        var canLoadMore = (this.$table.find('.final').length == 0)

        if (!hasUpdates || isLoading || !canLoadMore)
            return

        // technically, window.innerHeight includes the horizontal
        // scrollbar if present. oh well.
        var bottomOfTable = this.$table.offset().top + this.$table.height()
        var topOfLastScreenful = bottomOfTable - window.innerHeight
        var nearBottom = ($(window).scrollTop() + 250 >= topOfLastScreenful)

        if (nearBottom)
            this._loadMore()
    },

    _loadMore: function () {
        var lastId = this.$table.find('tr:last-child').data('fullname')

        // in case we get stuck in a loop somehow, bail out.
        if (lastId == this.lastFetchedId)
            return

        var params = $.param({
                'bare': 'y',
                'after': lastId,
                'count': this.$table.find('tr.thing').length
            })
        var url = '/live/' + r.config.liveupdate_event + '/?' + params

        this.$listing.addClass('loading')

        $.ajax({
            'url': url,
            'dataType': 'html'
        })
            .done($.proxy(function (response) {
                var $fragment = $(response),
                    $newRows = $fragment.find('.liveupdate-listing tbody').children()

                this.$listing.trigger('more-updates', [$newRows])
                this.$table.append($newRows)
                this.lastFetchedId = lastId

                r.timetext.refresh()
            }, this))
            .always($.proxy(function () {
                this.$listing.removeClass('loading')
            }, this))
    },

    _fetchPixel: function () {
        if (!this._pageVisible) {
            this._needToFetchPixel = true
            return
        }

        var pixel = new Image()
        pixel.src = '//' + r.config.liveupdate_pixel_domain +
                    '/live/' + r.config.liveupdate_event + '/pixel.png' +
                    '?rand=' + Math.random()

        var delay = Math.floor(this._pixelInterval -
                               this._pixelInterval * Math.random() / 2)
        setTimeout($.proxy(this, '_fetchPixel'), delay)
    }
}

r.liveupdate.init()
<|endoftext|>


<commit_before>const $ = require('jquery');

window.StaticConfig = window.StaticConfig || {};

class PublicClient {
  constructor (apiURI = '') {
    this.apiURI = apiURI;
  }

  get (...args) {
    return this.request('get', ...args);
  }

  put (...args) {
    return this.request('put', ...args);
  }

  post (...args) {
    return this.request('post', ...args);
  }

  delete (...args) {
    return this.request('delete', ...args);
  }

  addHeaders (obj, additional) {
    return Object.assign(
      {},
      obj.headers,
      additional
    );
  }

  paramsToURI (params) {
    const DEFAULT_PARAMS = '';

    return this.checkParams(params)
      ? `?${Object
        .keys(params)
        .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
        .join('&')}`
      : DEFAULT_PARAMS;
  }

  checkParams (params) {
    const OBJECT_TYPE = '[object Object]';

    return params &&
      Object.prototype.toString.call(params) === OBJECT_TYPE &&
      Object.keys(params).length;
  }

  makeRelativeURI (parts) {
    return `/${parts.join('/')}`;
  }

  makeAbsoluteURI (relativeURI) {
    return `${this.apiURI}${relativeURI}`;
  }

  successCallback (callback) {
    return (data, textStatus, jqXHR) => {
      try {
        data = JSON.parse(JSON.stringify(data));
      } catch (e) {
        data = null;
      }

      callback(null, textStatus, data);
    };
  }

  errorCallback (callback) {
    return (jqXHR, textStatus, errorThrown) => {
      const err = errorThrown || new Error('Failed to fetch');
      callback(err, textStatus, jqXHR);
    };
  }

  getAssetsBaseUrl () {
    const { host, protocol } = window.location;

    const regExp = window.location.href.match(/(\/(u|user)\/[a-z0-9\-]+)\//);
    const path = regExp && regExp[1] || '';

    return window.StaticConfig.baseUrl || `${protocol}//${host}${path}`;
  }

  request (method, uriParts, opts = {}, callback) {
    if (!callback && typeof opts === 'function') {
      callback = opts;
      opts = {};
    }

    Object.assign(opts, {
      'contentType': 'application/json; charset=utf-8',
      'method': method.toUpperCase()
    });

    this.addHeaders(opts);

    const baseUrl = this.getAssetsBaseUrl();
    const url = uriParts.length !== 0
      ? this.makeAbsoluteURI(this.makeRelativeURI(uriParts))
      : '';

    const requestOptions = {
      ...opts,
      success: this.successCallback(callback),
      error: this.errorCallback(callback)
    };
    $.ajax(`${baseUrl}${url}`, requestOptions);
  }
}

module.exports = exports = PublicClient;
<commit_msg>Remove object spread for Object.assign<commit_after>const $ = require('jquery');

window.StaticConfig = window.StaticConfig || {};

class PublicClient {
  constructor (apiURI = '') {
    this.apiURI = apiURI;
  }

  get (...args) {
    return this.request('get', ...args);
  }

  put (...args) {
    return this.request('put', ...args);
  }

  post (...args) {
    return this.request('post', ...args);
  }

  delete (...args) {
    return this.request('delete', ...args);
  }

  addHeaders (obj, additional) {
    return Object.assign(
      {},
      obj.headers,
      additional
    );
  }

  paramsToURI (params) {
    const DEFAULT_PARAMS = '';

    return this.checkParams(params)
      ? `?${Object
        .keys(params)
        .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`)
        .join('&')}`
      : DEFAULT_PARAMS;
  }

  checkParams (params) {
    const OBJECT_TYPE = '[object Object]';

    return params &&
      Object.prototype.toString.call(params) === OBJECT_TYPE &&
      Object.keys(params).length;
  }

  makeRelativeURI (parts) {
    return `/${parts.join('/')}`;
  }

  makeAbsoluteURI (relativeURI) {
    return `${this.apiURI}${relativeURI}`;
  }

  successCallback (callback) {
    return (data, textStatus, jqXHR) => {
      try {
        data = JSON.parse(JSON.stringify(data));
      } catch (e) {
        data = null;
      }

      callback(null, textStatus, data);
    };
  }

  errorCallback (callback) {
    return (jqXHR, textStatus, errorThrown) => {
      const err = errorThrown || new Error('Failed to fetch');
      callback(err, textStatus, jqXHR);
    };
  }

  getAssetsBaseUrl () {
    const { host, protocol } = window.location;

    const regExp = window.location.href.match(/(\/(u|user)\/[a-z0-9\-]+)\//);
    const path = regExp && regExp[1] || '';

    return window.StaticConfig.baseUrl || `${protocol}//${host}${path}`;
  }

  request (method, uriParts, opts = {}, callback) {
    if (!callback && typeof opts === 'function') {
      callback = opts;
      opts = {};
    }

    Object.assign(opts, {
      'contentType': 'application/json; charset=utf-8',
      'method': method.toUpperCase()
    });

    this.addHeaders(opts);

    const baseUrl = this.getAssetsBaseUrl();
    const url = uriParts.length !== 0
      ? this.makeAbsoluteURI(this.makeRelativeURI(uriParts))
      : '';

    const requestOptions = Object.assign({}, opts,
      {
        success: this.successCallback(callback),
        error: this.errorCallback(callback)
      }
    );

    $.ajax(`${baseUrl}${url}`, requestOptions);
  }
}

module.exports = exports = PublicClient;
<|endoftext|>


<commit_before>import UI from 'src/util/ui';

import { convert } from '../libs/jcampconverter';

async function jcampInfo(value) {
  let jcamp = await DataObject.check(value.jcamp.data, true).get(true);

  let parsed = convert(String(jcamp), {
    withoutXY: true,
    keepRecordsRegExp: /.*/
  });

  let data = Object.keys(parsed.info).map((key) => {
    return { label: key, value: parsed.info[key] };
  });

  let html = `

        Search parameters: <input type='text' oninput='filter(this)'>

                ${data
    .map(
      (datum) => `

                        ${datum.label}

${
  datum.value.replace
    ? datum.value.replace(/[\r\n]+$/, '')
    : datum.value
}

                `
    )
    .join('\n')}

    `;

  UI.dialog(html, {
    width: 800,
    height: 600,
    title: 'List of parameters'
  });
}

module.exports = jcampInfo;
<commit_msg>fix: deal correclty with jcamp array<commit_after>import UI from "src/util/ui";

import { convert } from "../libs/jcampconverter";

async function jcampInfo(value) {
  let jcamp = await DataObject.check(value.jcamp.data, true).get(true);

  let parsed = convert(String(jcamp), {
    withoutXY: true,
    keepRecordsRegExp: /.*/
  });

  let data = [];

  for (let key of Object.keys(parsed.info)) {
    let value = parsed.info[key];
    if (Array.isArray(value)) {
      for (let i = 0; i < value.length; i++) {
        data.push({ label: key + "." + (i + 1), value: value[i] });
      }
    } else {
      data.push({ label: key, value });
    }
  }

  let html = `

        Search parameters: <input type='text' oninput='filter(this)'>

                ${data
                  .map(
                    datum => `

                        ${datum.label}

${
                          datum.value.replace
                            ? datum.value.replace(/[\r\n]+$/, "")
                            : datum.value
                        }

                `
                  )
                  .join("\n")}

    `;

  UI.dialog(html, {
    width: 800,
    height: 600,
    title: "List of parameters"
  });
}

module.exports = jcampInfo;
<|endoftext|>


<commit_before>#!/usr/bin/env node
/*global require, process, console, JSON, module, Buffer, __dirname*/

/**
 * Module dependencies.
 */

var express   = require('express'),
    fs        = require('fs'),
    qs        = require('qs'),
    commander = require('commander'),
    Hydra     = require('../lib/hydra').Hydra;

commander.version('0.0.1').
    usage("mysetup.conf [confvar=value confvar2=value2 ...]").
    option('-I <path>', 'Adds a new path in the plugin search path list').
    option('-p, --port <port>', 'Listen on this port (default 3000)', 3000).
    parse(process.argv);

// This is a bit crappy, as it uses the global commander variable. But whaeva.
function showHelpAndDie(message) {
    if (message) {
        console.log(message);
    }
    console.log(commander.helpInformation());
    process.exit(1);
}

var hydra = new Hydra();

// Check parameters and load Hydra configuration
if (commander.args.length < 1)
    showHelpAndDie();
var configPath = commander.args[0];
var hydraConfigString = fs.readFileSync(configPath, 'utf-8');
var hydraConfig = JSON.parse(hydraConfigString);
if (! hydraConfig.plugins) {
    showHelpAndDie(configPath + " doesn't seem like a valid Hydra plugin (missing 'plugins' property in the top-level object)");
}
// Process the options
if (commander.I) {
    hydra.addPluginLoadPath(commander.I);
}
// After the second parameter, the rest is extra configuration variables
var extraVars = {};
for (var i = 1, len = commander.args.length; i < len; i++) {
    var varAndValue  = commander.args[i].split('=', 2);
    if (varAndValue.length !== 2) {
        showHelpAndDie();
    } else {
        extraVars[varAndValue[0]] = varAndValue[1];
    }
}

hydraConfig.plugins.forEach(function(pluginDef) {
    var config = {}, p;
    for (p in pluginDef.config) config[p] = pluginDef.config[p];
    for (p in extraVars) config[p] = extraVars[p];
    var plugin = hydra.requirePlugin(pluginDef.name, config);

    var pluginObject = plugin.module.getBodyParts(plugin.config);
    pluginObject.name = pluginDef.name;
    hydra.registerPluginObject(pluginObject);

    var featureMessages = [];
    if (typeof pluginObject.heads === 'object') {
        featureMessages.push(pluginObject.heads.length + " head(s)");
    }
    if (typeof pluginObject.tests === 'object') {
        var testCount = 0;
        for (var test in pluginObject.tests) {
            if (pluginObject.tests.hasOwnProperty(test)) {
                testCount++;
            }
        }
        featureMessages.push(testCount + " test(s)");
    }
    console.log("Registering hydra plugin " + pluginObject.name + " (" +
                featureMessages.join(", ") + ")");
});

var app = module.exports = express.createServer(express.logger());

// Configuration
app.configure(function(){
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
    app.use(express.errorHandler());
});

// Routes are all dynamic, so we only need a catch-all here
app.all('/*', function(expressReq, expressRes) {
    var req = {
        url: expressReq.url,
        getParams: expressReq.query,
        method: expressReq.method,
        headers: expressReq.headers,
        rawBody: new Buffer("")
    };
    var res = {
        body: new Buffer(0),
        write: function(chunk) {
            var tmp = new Buffer(this.body.length + chunk.length);
            this.body.copy(tmp);
            chunk.copy(tmp, this.body.length);
            this.body = tmp;
        },
        send: function(data) { this.write(data); this.end(); },
        end: function() {
            expressRes.writeHead(res.statusCode, res.headers);
            if (res.body !== undefined) expressRes.write(res.body);
            expressRes.end()
        },
        statusCode: 200,
        headers: {}
    };

    // Fetch POST data if available
    expressReq.addListener("data", function (chunk) {
        var tmp = new Buffer(req.rawBody.length + chunk.length);
        req.rawBody.copy(tmp);
        chunk.copy(tmp, req.rawBody.length);
        req.rawBody = tmp;
    });
    expressReq.addListener("end", function () {
        // Try to parse the body...
        try {
            req.bodyParams = qs.parse(req.rawBody.toString());
        } catch(e) {
            // but it's ok if qs can't handle it
        }
        // When we have a complete request, dispatch it through Hydra
        hydra.handle(req, res);
    });
});

app.listen(commander.port);
if (app.address()) {
    console.log("Express server listening on port %d in %s mode",
                app.address().port,
                app.settings.env);
} else {
    console.log("Couldn't listen in port %s", commander.port);
}
<commit_msg>Support both plain strings and buffers<commit_after>#!/usr/bin/env node
/*global require, process, console, JSON, module, Buffer, __dirname*/

/**
 * Module dependencies.
 */

var express   = require('express'),
    fs        = require('fs'),
    qs        = require('qs'),
    commander = require('commander'),
    Hydra     = require('../lib/hydra').Hydra;

commander.version('0.0.1').
    usage("mysetup.conf [confvar=value confvar2=value2 ...]").
    option('-I <path>', 'Adds a new path in the plugin search path list').
    option('-p, --port <port>', 'Listen on this port (default 3000)', 3000).
    parse(process.argv);

// This is a bit crappy, as it uses the global commander variable. But whaeva.
function showHelpAndDie(message) {
    if (message) {
        console.log(message);
    }
    console.log(commander.helpInformation());
    process.exit(1);
}

var hydra = new Hydra();

// Check parameters and load Hydra configuration
if (commander.args.length < 1)
    showHelpAndDie();
var configPath = commander.args[0];
var hydraConfigString = fs.readFileSync(configPath, 'utf-8');
var hydraConfig = JSON.parse(hydraConfigString);
if (! hydraConfig.plugins) {
    showHelpAndDie(configPath + " doesn't seem like a valid Hydra plugin (missing 'plugins' property in the top-level object)");
}
// Process the options
if (commander.I) {
    hydra.addPluginLoadPath(commander.I);
}
// After the second parameter, the rest is extra configuration variables
var extraVars = {};
for (var i = 1, len = commander.args.length; i < len; i++) {
    var varAndValue  = commander.args[i].split('=', 2);
    if (varAndValue.length !== 2) {
        showHelpAndDie();
    } else {
        extraVars[varAndValue[0]] = varAndValue[1];
    }
}

hydraConfig.plugins.forEach(function(pluginDef) {
    var config = {}, p;
    for (p in pluginDef.config) config[p] = pluginDef.config[p];
    for (p in extraVars) config[p] = extraVars[p];
    var plugin = hydra.requirePlugin(pluginDef.name, config);

    var pluginObject = plugin.module.getBodyParts(plugin.config);
    pluginObject.name = pluginDef.name;
    hydra.registerPluginObject(pluginObject);

    var featureMessages = [];
    if (typeof pluginObject.heads === 'object') {
        featureMessages.push(pluginObject.heads.length + " head(s)");
    }
    if (typeof pluginObject.tests === 'object') {
        var testCount = 0;
        for (var test in pluginObject.tests) {
            if (pluginObject.tests.hasOwnProperty(test)) {
                testCount++;
            }
        }
        featureMessages.push(testCount + " test(s)");
    }
    console.log("Registering hydra plugin " + pluginObject.name + " (" +
                featureMessages.join(", ") + ")");
});

var app = module.exports = express.createServer(express.logger());

// Configuration
app.configure(function(){
    app.set('views', __dirname + '/views');
    app.set('view engine', 'jade');
    app.use(app.router);
    app.use(express.static(__dirname + '/public'));
});
app.configure('development', function(){
    app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
    app.use(express.errorHandler());
});

// Routes are all dynamic, so we only need a catch-all here
app.all('/*', function(expressReq, expressRes) {
    var req = {
        url: expressReq.url,
        getParams: expressReq.query,
        method: expressReq.method,
        headers: expressReq.headers,
        rawBody: new Buffer("")
    };
    var res = {
        body: new Buffer(0),
        write: function(chunk) {
            if (typeof chunk === 'string') {
                chunk = new Buffer(chunk);
            }
            var tmp = new Buffer(this.body.length + chunk.length);
            this.body.copy(tmp);
            chunk.copy(tmp, this.body.length);
            this.body = tmp;
        },
        send: function(data) { this.write(data); this.end(); },
        end: function() {
            expressRes.writeHead(res.statusCode, res.headers);
            if (res.body !== undefined) expressRes.write(res.body);
            expressRes.end()
        },
        statusCode: 200,
        headers: {}
    };

    // Fetch POST data if available
    expressReq.addListener("data", function (chunk) {
        var tmp = new Buffer(req.rawBody.length + chunk.length);
        req.rawBody.copy(tmp);
        chunk.copy(tmp, req.rawBody.length);
        req.rawBody = tmp;
    });
    expressReq.addListener("end", function () {
        // Try to parse the body...
        try {
            req.bodyParams = qs.parse(req.rawBody.toString());
        } catch(e) {
            // but it's ok if qs can't handle it
        }
        // When we have a complete request, dispatch it through Hydra
        hydra.handle(req, res);
    });
});

app.listen(commander.port);
if (app.address()) {
    console.log("Express server listening on port %d in %s mode",
                app.address().port,
                app.settings.env);
} else {
    console.log("Couldn't listen in port %s", commander.port);
}
<|endoftext|>


<commit_before>"use strict";

var createBlockAndLogStreamer = require("./block-management/create-block-and-log-streamer");
var createTransportAdapter = require("./block-management/ethrpc-transport-adapter");
var onNewBlock = require("./block-management/on-new-block");
var internalState = require("./internal-state");

module.exports = function (startingBlockNumber, callback) {
  return function (dispatch, getState) {
    var storedConfiguration = getState().configuration;
    dispatch(createBlockAndLogStreamer({
      pollingIntervalMilliseconds: storedConfiguration.pollingIntervalMilliseconds,
      blockRetention: storedConfiguration.blockRetention,
      startingBlockNumber: startingBlockNumber
    }, dispatch(createTransportAdapter(internalState.get("transporter"))), internalState.get("outOfBandErrorHandler"), callback));

    internalState.get("blockAndLogStreamer").subscribeToOnBlockAdded(function (block) {
      dispatch(onNewBlock(block));
    });
  };
};
<commit_msg>Pass non-out of band callback into startBlockStream<commit_after>"use strict";

var createBlockAndLogStreamer = require("./block-management/create-block-and-log-streamer");
var createTransportAdapter = require("./block-management/ethrpc-transport-adapter");
var onNewBlock = require("./block-management/on-new-block");
var internalState = require("./internal-state");

module.exports = function (startingBlockNumber, callback) {
  return function (dispatch, getState) {
    var storedConfiguration = getState().configuration;
    dispatch(createBlockAndLogStreamer({
      pollingIntervalMilliseconds: storedConfiguration.pollingIntervalMilliseconds,
      blockRetention: storedConfiguration.blockRetention,
      startingBlockNumber: startingBlockNumber
    }, dispatch(createTransportAdapter(internalState.get("transporter"))), callback));

    internalState.get("blockAndLogStreamer").subscribeToOnBlockAdded(function (block) {
      dispatch(onNewBlock(block));
    });
  };
};
<|endoftext|>


<commit_before>/*jshint -W030 */
/* global
 DEBUG:true
 */

// empty class for mirror implementations to extend
Velocity.Mirror = {};

DEBUG = !!process.env.VELOCITY_DEBUG;

(function () {

  'use strict';

  if (process.env.NODE_ENV !== 'development' ||
    process.env.IS_MIRROR) {
    DEBUG && console.log('[velocity] Not adding mirror-registry because NODE_ENV is',
      process.env.NODE_ENV, ' and IS_MIRROR is', process.env.IS_MIRROR);
    return;
  }

  var _ = Npm.require('lodash'),
      url = Npm.require('url');

//////////////////////////////////////////////////////////////////////
// Public Methods
//

  _.extend(Velocity.Mirror, {

    // TODO This can be extended to support multiple mirror types, but just one for now until we
    // get the API right
    start: function () {
      throw new Error('Mirror requested but a mirror has not been implemented');
    }

  });

//////////////////////////////////////////////////////////////////////
// Meteor Methods
//

  /**
   * Most communication with Velocity core is done via the following
   * Meteor methods.
   *
   * @class Meteor.methods
   */
  Meteor.methods({

    /**
     * Starts a new mirror if it has not already been started, and reuses an
     * existing one if it is already started.
     *
     * This method will update the `VelocityMirrors` collection with once the mirror is ready.
     *
     * @method velocity/mirrors/request
     *
     * @param {Object} options                  Options for the mirror.
     * @param {String} options.framework        The name of the calling framework
     * @param {String} [options.port]           String use a specific port
     * @param {String} [options.rootUrlPath]    Adds this string to the end of the root url in the
     *                                          VelocityMirrors collection. eg. `/?jasmine=true`
     *                 [options.mirrorId]       An id for this mirror that will be stored in the
     *                                          VelocityMirrors collection and also available as
     *                                          MIRROR_ID environment var from the mirror.
     *                                          If not provided, this will be set to a random
     *                                          mongo-style id.
     *
     * @return {String}   The mirrorId. Frameworks can wait for this mirrorId to be ready should
     *                    they need to.
     *
     */
    'velocity/mirrors/request': function (options) {
      check(options, {
        framework: String,
        port: Match.Optional(Number),
        rootUrlPath: Match.Optional(String),
        mirrorId: Match.Optional(String)
      });

      options.mirrorId = options.mirrorId || Random.id();

      _startOrReuseMirror(options);

      return options.mirrorId;
    },

    /**
     * Stores metadata about the mirror. Before a mirror implementation starts, it needs to call
     * this method to let Velocity know it is starting up.
     *
     * @param options {Object}
     *            Required fields
     *                mirrorId  : the ID of this mirror. This is provided to mirrors in the
     *                            startOrReuseMirror call in this class
     *                port      : the port this mirror is running on
     *                mongoUrl  : the mongo url this mirror is using
     *                host      : the root url of this mirror without any additional paths. Used for
     *                            making DDP connections
     *                rootUrl   : the root url of this mirror, which also includes the path and params
     *                type      : eg 'node-soft-mirror' or 'meteor-soft-mirror'
     * @param extra {Object}    Any additional metadata the implementing mirror would like to store
     *                          in the Velocity mirrors collection. This is optional.
     *
     */
    'velocity/mirrors/init': function (options, extra) {
      check(options, {
        mirrorId: String,
        port: Number,
        mongoUrl: String,
        host: String,
        rootUrl: String,
        type: String
      });

      if (extra) {
        _.extend(options, extra);
      }

      VelocityMirrors.insert(_.extend(options, {
        _id: options.mirrorId,
        reused: false,
        state: 'starting'
      }));
    },

    /**
     * Lets Velocity know the mirror has started successfully
     *
     * @param options
     *            Required fields
     *                mirrorId  : the ID this mirror was given by
     *                port      : the port this mirror is running on
     */
    'velocity/mirrors/register': function (options) {

      DEBUG && console.log('[velocity] Connecting to mirror');

      // Ugliness! It seems the DDP connect has a exponential back-off, which means the tests take
      // around 5 seconds to rerun. This setTimeout helps.
      Meteor.setTimeout(function() {
        var mirrorConnection = DDP.connect(options.host);
        mirrorConnection.onReconnect = function () {
          DEBUG && console.log('[velocity] Connected to mirror, setting state to ready', options);
          VelocityMirrors.update({
            framework: options.framework,
            port: parseInt(options.port)
          }, {
            $set: {
              state: 'ready'
            }
          });
        };
      }, 300);

    },

    /**
     * Exposes the IS_MIRROR flag to clients
     *
     * @method velocity/isMirror
     * @return {Boolean} true if currently running in mirror
     */
    'velocity/isMirror': function () {
      return !!process.env.IS_MIRROR;
    }

  });  // end Meteor methods

//////////////////////////////////////////////////////////////////////
// Private functions
//

  /**
   * Starts a new mirror if one is not found. The criteria to reuse a mirror is that the requested
   * mirror and an existing mirror share the same port. If the same port is found, a new entry in
   * the mirrors collection is added with the new metadata but the same port. If a mirror is reused,
   * the metadata in the VelocityMirrors will have the property 'reused' set to true.
   *
   * @method startOrReuseMirror
   * @param {Object} options
   *                Required fields:
   *                   framework - String ex. 'mocha-web-1'
   *                   rootUrlPath - String ex. '/x=y'
   *                   port - a specific port to start the mirror on
   *                   mirrorId - the ID this miror was given in the requestMirror call
   *
   * @private
   */
  function _startOrReuseMirror (options) {

    options.port = options.port || process.env.DEFAULT_MIRROR_PORT || 5000;
    var rootUrlPath = (options.rootUrlPath || '').replace(/\//, '');
    options.host = _getMirrorUrl(options.port);
    options.rootUrl = options.host + rootUrlPath;

    DEBUG && console.log('[velocity] Mirror requested', options);

    var mirrorWithSamePort = VelocityMirrors.findOne({port: parseInt(options.port)});
    if (mirrorWithSamePort && mirrorWithSamePort.framework !== options.framework) {
      // the ports only match, which means this is a new framework reusing an existing mirror
      delete mirrorWithSamePort._id;
      mirrorWithSamePort.framework = options.framework;
      mirrorWithSamePort.rootUrlPath = options.rootUrlPath;
      mirrorWithSamePort.reused = true;
      VelocityMirrors.insert(mirrorWithSamePort);
    } else if (!mirrorWithSamePort) {
      // delegate the mirror start to the framework-chose mirror choice
      Velocity.Mirror.start(options, _getEnvironmentVariables(options));
    }
  }

  /**
   * Returns the MongoDB URL for the given database.
   * @param database
   * @returns {string} MongoDB Url
   * @private
   */
  function _getMongoUrl (database) {
    var mongoLocationParts = url.parse(process.env.MONGO_URL);
    return url.format({
      protocol: mongoLocationParts.protocol,
      slashes: mongoLocationParts.slashes,
      hostname: mongoLocationParts.hostname,
      port: mongoLocationParts.port,
      pathname: '/' + database
    });
  }

  /**
   * Return URL for the mirror with the given port.
   * @param port Mirror port
   * @returns {string} Mirror URL
   * @private
   */
  function _getMirrorUrl (port) {
    var rootUrlParts = url.parse(Meteor.absoluteUrl());
    return url.format({
      protocol: rootUrlParts.protocol,
      slashes: rootUrlParts.slashes,
      hostname: rootUrlParts.hostname,
      port: port,
      pathname: rootUrlParts.pathname
    });
  }

  /**
   * Return the environment variables that a mirror should run with
   * @param {Object} options Required fields:
   *                   framework - String ex. 'mocha-web-1'
   *                   rootUrl - String ex. 'http://localhost:5000/x=y'
   *                   port - a specific port
   * @returns {string} Mirror URL
   * @private
   */
  function _getEnvironmentVariables (options) {
    // perhaps we can copy the meteor environment directly here?
    return {
      NODE_ENV: process.env.NODE_ENV,
      DEBUG: DEBUG,
      VELOCITY_DEBUG: process.env.VELOCITY_DEBUG,
      PORT: options.port,
      HOST: options.host,
      ROOT_URL: options.rootUrl,
      MONGO_URL: _getMongoUrl(options.framework),
      PARENT_URL: process.env.ROOT_URL,
      MIRROR_ID: options.mirrorId,
      IS_MIRROR: true,
      METEOR_SETTINGS: JSON.stringify(_.extend({}, Meteor.settings))
    };
  }

})();
<commit_msg>adding last modified<commit_after>/*jshint -W030 */
/* global
 DEBUG:true
 */

// empty class for mirror implementations to extend
Velocity.Mirror = {};

DEBUG = !!process.env.VELOCITY_DEBUG;

(function () {

  'use strict';

  if (process.env.NODE_ENV !== 'development' ||
    process.env.IS_MIRROR) {
    DEBUG && console.log('[velocity] Not adding mirror-registry because NODE_ENV is',
      process.env.NODE_ENV, ' and IS_MIRROR is', process.env.IS_MIRROR);
    return;
  }

  var _ = Npm.require('lodash'),
      url = Npm.require('url');

//////////////////////////////////////////////////////////////////////
// Public Methods
//

  _.extend(Velocity.Mirror, {

    // TODO This can be extended to support multiple mirror types, but just one for now until we
    // get the API right
    start: function () {
      throw new Error('Mirror requested but a mirror has not been implemented');
    }

  });

//////////////////////////////////////////////////////////////////////
// Meteor Methods
//

  /**
   * Most communication with Velocity core is done via the following
   * Meteor methods.
   *
   * @class Meteor.methods
   */
  Meteor.methods({

    /**
     * Starts a new mirror if it has not already been started, and reuses an
     * existing one if it is already started.
     *
     * This method will update the `VelocityMirrors` collection with once the mirror is ready.
     *
     * @method velocity/mirrors/request
     *
     * @param {Object} options                  Options for the mirror.
     * @param {String} options.framework        The name of the calling framework
     * @param {String} [options.port]           String use a specific port
     * @param {String} [options.rootUrlPath]    Adds this string to the end of the root url in the
     *                                          VelocityMirrors collection. eg. `/?jasmine=true`
     *                 [options.mirrorId]       An id for this mirror that will be stored in the
     *                                          VelocityMirrors collection and also available as
     *                                          MIRROR_ID environment var from the mirror.
     *                                          If not provided, this will be set to a random
     *                                          mongo-style id.
     *
     * @return {String}   The mirrorId. Frameworks can wait for this mirrorId to be ready should
     *                    they need to.
     *
     */
    'velocity/mirrors/request': function (options) {
      check(options, {
        framework: String,
        port: Match.Optional(Number),
        rootUrlPath: Match.Optional(String),
        mirrorId: Match.Optional(String)
      });

      options.mirrorId = options.mirrorId || Random.id();

      _startOrReuseMirror(options);

      return options.mirrorId;
    },

    /**
     * Stores metadata about the mirror. Before a mirror implementation starts, it needs to call
     * this method to let Velocity know it is starting up.
     *
     * @param options {Object}
     *            Required fields
     *                mirrorId  : the ID of this mirror. This is provided to mirrors in the
     *                            startOrReuseMirror call in this class
     *                port      : the port this mirror is running on
     *                mongoUrl  : the mongo url this mirror is using
     *                host      : the root url of this mirror without any additional paths. Used for
     *                            making DDP connections
     *                rootUrl   : the root url of this mirror, which also includes the path and params
     *                type      : eg 'node-soft-mirror' or 'meteor-soft-mirror'
     * @param extra {Object}    Any additional metadata the implementing mirror would like to store
     *                          in the Velocity mirrors collection. This is optional.
     *
     */
    'velocity/mirrors/init': function (options, extra) {
      check(options, {
        mirrorId: String,
        port: Number,
        mongoUrl: String,
        host: String,
        rootUrl: String,
        type: String
      });

      if (extra) {
        _.extend(options, extra);
      }

      VelocityMirrors.insert(_.extend(options, {
        _id: options.mirrorId,
        reused: false,
        state: 'starting'
      }));
    },

    /**
     * Lets Velocity know the mirror has started successfully
     *
     * @param options
     *            Required fields
     *                mirrorId  : the ID this mirror was given by
     *                port      : the port this mirror is running on
     */
    'velocity/mirrors/register': function (options) {

      DEBUG && console.log('[velocity] Connecting to mirror');

      // Ugliness! It seems the DDP connect has a exponential back-off, which means the tests take
      // around 5 seconds to rerun. This setTimeout helps.
      Meteor.setTimeout(function() {
        var mirrorConnection = DDP.connect(options.host);
        mirrorConnection.onReconnect = function () {
          DEBUG && console.log('[velocity] Connected to mirror, setting state to ready', options);
          VelocityMirrors.update({
            framework: options.framework,
            port: parseInt(options.port)
          }, {
            $set: {
              state: 'ready',
              lastModified: Date.now()
            }
          });
        };
      }, 300);

    },

    /**
     * Exposes the IS_MIRROR flag to clients
     *
     * @method velocity/isMirror
     * @return {Boolean} true if currently running in mirror
     */
    'velocity/isMirror': function () {
      return !!process.env.IS_MIRROR;
    }

  });  // end Meteor methods

//////////////////////////////////////////////////////////////////////
// Private functions
//

  /**
   * Starts a new mirror if one is not found. The criteria to reuse a mirror is that the requested
   * mirror and an existing mirror share the same port. If the same port is found, a new entry in
   * the mirrors collection is added with the new metadata but the same port. If a mirror is reused,
   * the metadata in the VelocityMirrors will have the property 'reused' set to true.
   *
   * @method startOrReuseMirror
   * @param {Object} options
   *                Required fields:
   *                   framework - String ex. 'mocha-web-1'
   *                   rootUrlPath - String ex. '/x=y'
   *                   port - a specific port to start the mirror on
   *                   mirrorId - the ID this miror was given in the requestMirror call
   *
   * @private
   */
  function _startOrReuseMirror (options) {

    options.port = options.port || process.env.DEFAULT_MIRROR_PORT || 5000;
    var rootUrlPath = (options.rootUrlPath || '').replace(/\//, '');
    options.host = _getMirrorUrl(options.port);
    options.rootUrl = options.host + rootUrlPath;

    DEBUG && console.log('[velocity] Mirror requested', options);

    var mirrorWithSamePort = VelocityMirrors.findOne({port: parseInt(options.port)});
    if (mirrorWithSamePort && mirrorWithSamePort.framework !== options.framework) {
      // the ports only match, which means this is a new framework reusing an existing mirror
      delete mirrorWithSamePort._id;
      mirrorWithSamePort.framework = options.framework;
      mirrorWithSamePort.rootUrlPath = options.rootUrlPath;
      mirrorWithSamePort.reused = true;
      VelocityMirrors.insert(mirrorWithSamePort);
    } else if (!mirrorWithSamePort) {
      // delegate the mirror start to the framework-chose mirror choice
      Velocity.Mirror.start(options, _getEnvironmentVariables(options));
    }
  }

  /**
   * Returns the MongoDB URL for the given database.
   * @param database
   * @returns {string} MongoDB Url
   * @private
   */
  function _getMongoUrl (database) {
    var mongoLocationParts = url.parse(process.env.MONGO_URL);
    return url.format({
      protocol: mongoLocationParts.protocol,
      slashes: mongoLocationParts.slashes,
      hostname: mongoLocationParts.hostname,
      port: mongoLocationParts.port,
      pathname: '/' + database
    });
  }

  /**
   * Return URL for the mirror with the given port.
   * @param port Mirror port
   * @returns {string} Mirror URL
   * @private
   */
  function _getMirrorUrl (port) {
    var rootUrlParts = url.parse(Meteor.absoluteUrl());
    return url.format({
      protocol: rootUrlParts.protocol,
      slashes: rootUrlParts.slashes,
      hostname: rootUrlParts.hostname,
      port: port,
      pathname: rootUrlParts.pathname
    });
  }

  /**
   * Return the environment variables that a mirror should run with
   * @param {Object} options Required fields:
   *                   framework - String ex. 'mocha-web-1'
   *                   rootUrl - String ex. 'http://localhost:5000/x=y'
   *                   port - a specific port
   * @returns {string} Mirror URL
   * @private
   */
  function _getEnvironmentVariables (options) {
    // perhaps we can copy the meteor environment directly here?
    return {
      NODE_ENV: process.env.NODE_ENV,
      DEBUG: DEBUG,
      VELOCITY_DEBUG: process.env.VELOCITY_DEBUG,
      PORT: options.port,
      HOST: options.host,
      ROOT_URL: options.rootUrl,
      MONGO_URL: _getMongoUrl(options.framework),
      PARENT_URL: process.env.ROOT_URL,
      MIRROR_ID: options.mirrorId,
      IS_MIRROR: true,
      METEOR_SETTINGS: JSON.stringify(_.extend({}, Meteor.settings))
    };
  }

})();
<|endoftext|>


<commit_before>/**
 * nashorn-promise
 *
 * @author hidekatsu.izuno@gmail.com (Hidekatsu Izuno)
 * @license MIT License
 */

(function(global) {
	'use strict';

	if (global.Promise === undefined) {
		var JExecutors = Java.type('java.util.concurrent.Executors');
		var JCompletableFuture = Java.type('java.util.concurrent.CompletableFuture');
		var JCompleteFutureArray = Java.type('java.util.concurrent.CompletableFuture[]');
		var JPromiseException = Java.type('net.arnx.nashorn.lib.PromiseException');

		var pool = JExecutors.newCachedThreadPool();

		var Promise = function(resolver, futures) {
			var that = this;
			if (resolver instanceof JCompletableFuture) {
				that._future = resolver;
				that._futures = futures;
			} else {
				that._future = JCompletableFuture.supplyAsync(function() {
					var status, result;
					(0, resolver)(function(value) {
						status = 'fulfilled';
						result = value;
					}, function(reason) {
						status = 'rejected';
						result = reason;
					});
					if (status == 'fulfilled') {
						return {
							result: result
						};
					} else if (status == 'rejected') {
						throw new JPromiseException(result);
					}
				}, pool);
			}
		};

		Promise.all = function(array) {
			var futures = array.map(function(elem) {
				if (elem instanceof Promise) {
					return elem._future;
				}
				return Promise.resolve(elem)._future;
			});
			return new Promise(JCompletableFuture.allOf(Java.to(futures, JCompleteFutureArray)), futures);
		};

		Promise.race = function(array) {
			var futures = array.map(function(elem) {
				if (elem instanceof Promise) {
					return elem._future;
				}
				return Promise.resolve(elem)._future;
			});
			return new Promise(JCompletableFuture.anyOf(Java.to(futures, JCompleteFutureArray)));
		};

		Promise.resolve = function(value) {
			if (value instanceof Promise) {
				return value;
			} else if (value != null
					&& (typeof value === 'function' || typeof value === 'object')
					&& typeof value.then === 'function') {
				return new Promise(function(fulfill, reject) {
					try {
						return {
							result: value.then(fulfill, reject)
						}
					} catch (e) {
						throw new JPromiseException(e);
					}
				});
			} else {
				return new Promise(JCompletableFuture.completedFuture({
					result: value
				}));
			}
		};

		Promise.reject = function(value) {
			return new Promise(function(fulfill, reject) {
				reject(value);
			});
		};

		Promise.prototype.then = function(onFulfillment, onRejection) {
			var that = this;
			return new Promise(that._future.handle(function(success, error) {
				if (success == null && error == null && that._futures != null) {
					success = {
						result: that._futures.map(function(elem) {
							return elem.get().result;
						})
					};
				}

				if (success != null) {
					if (typeof onFulfillment === 'function') {
						try {
							return {
								result: (0, onFulfillment)(success.result)
							};
						} catch (e) {
							throw new JPromiseException(e)
						}
					}
					return success;
				} else if (error != null) {
					var cerror = error;
					do {
						if (cerror instanceof JPromiseException) {
							error = cerror;
							break;
						}
					} while ((cerror = cerror.getCause()) != null);

					if (typeof onRejection === 'function') {
						try {
							var reason  = error;
							if (error instanceof JPromiseException) {
								reason = error.getResult();
							}

							return {
								result: (0, onRejection)(reason)
							};
						} catch (e) {
							throw new JPromiseException(e)
						}
					}
					throw error;
				}
			}));
		};

		Promise.prototype.catch = function(onRejection) {
			return this.then(null, onRejection);
		};

		global.Promise = Promise;
	}
})(Function('return this')());<commit_msg>It's posible to change thread pool<commit_after>/**
 * nashorn-promise
 *
 * @author hidekatsu.izuno@gmail.com (Hidekatsu Izuno)
 * @license MIT License
 */

(function(global) {
	'use strict';

	if (global.Promise === undefined) {
		var JCompletableFuture = Java.type('java.util.concurrent.CompletableFuture');
		var JCompleteFutureArray = Java.type('java.util.concurrent.CompletableFuture[]');
		var JPromiseException = Java.type('net.arnx.nashorn.lib.PromiseException');

		var Promise = function(resolver, futures) {
			var that = this;
			if (resolver instanceof JCompletableFuture) {
				that._future = resolver;
				that._futures = futures;
			} else {
				var func = function() {
					var status, result;
					(0, resolver)(function(value) {
						status = 'fulfilled';
						result = value;
					}, function(reason) {
						status = 'rejected';
						result = reason;
					});
					if (status == 'fulfilled') {
						return {
							result: result
						};
					} else if (status == 'rejected') {
						throw new JPromiseException(result);
					}
				};
				if (Promise._pool) {
					that._future = JCompletableFuture.supplyAsync(func, Promise._pool);
				} else {
					that._future = JCompletableFuture.supplyAsync(func);
				}
			}
		};

		Promise.all = function(array) {
			var futures = array.map(function(elem) {
				if (elem instanceof Promise) {
					return elem._future;
				}
				return Promise.resolve(elem)._future;
			});
			return new Promise(JCompletableFuture.allOf(Java.to(futures, JCompleteFutureArray)), futures);
		};

		Promise.race = function(array) {
			var futures = array.map(function(elem) {
				if (elem instanceof Promise) {
					return elem._future;
				}
				return Promise.resolve(elem)._future;
			});
			return new Promise(JCompletableFuture.anyOf(Java.to(futures, JCompleteFutureArray)));
		};

		Promise.resolve = function(value) {
			if (value instanceof Promise) {
				return value;
			} else if (value != null
					&& (typeof value === 'function' || typeof value === 'object')
					&& typeof value.then === 'function') {
				return new Promise(function(fulfill, reject) {
					try {
						return {
							result: value.then(fulfill, reject)
						}
					} catch (e) {
						throw new JPromiseException(e);
					}
				});
			} else {
				return new Promise(JCompletableFuture.completedFuture({
					result: value
				}));
			}
		};

		Promise.reject = function(value) {
			return new Promise(function(fulfill, reject) {
				reject(value);
			});
		};

		Promise.prototype.then = function(onFulfillment, onRejection) {
			var that = this;
			return new Promise(that._future.handle(function(success, error) {
				if (success == null && error == null && that._futures != null) {
					success = {
						result: that._futures.map(function(elem) {
							return elem.get().result;
						})
					};
				}

				if (success != null) {
					if (typeof onFulfillment === 'function') {
						try {
							return {
								result: (0, onFulfillment)(success.result)
							};
						} catch (e) {
							throw new JPromiseException(e)
						}
					}
					return success;
				} else if (error != null) {
					var cerror = error;
					do {
						if (cerror instanceof JPromiseException) {
							error = cerror;
							break;
						}
					} while ((cerror = cerror.getCause()) != null);

					if (typeof onRejection === 'function') {
						try {
							var reason  = error;
							if (error instanceof JPromiseException) {
								reason = error.getResult();
							}

							return {
								result: (0, onRejection)(reason)
							};
						} catch (e) {
							throw new JPromiseException(e)
						}
					}
					throw error;
				}
			}));
		};

		Promise.prototype.catch = function(onRejection) {
			return this.then(null, onRejection);
		};

		global.Promise = Promise;
	}
})(Function('return this')());<|endoftext|>