<commit_before>'use strict';
var fs = require("fs");
var uuid = require('uuid');
var userCollectionName = "users";
var groupsCollectionName = "groups";

var guest = require("./guest.js");
var account = require("./account.js");
var user = require("./user.js");
var group = require("./group.js");
var token = require("./token.js");

var utils = require("./utils.js");

var Mongo = require("soajs").mongo;
var mongoCore = null;

var soajs = require("soajs");
var Auth = soajs.authorization;

var async = require('async');

function checkIfTenantIsDBTN(req, cb) {
	if (!mongoCore) {
		mongoCore = new Mongo(req.soajs.registry.coreDB.provision);
	}

	if (!req.soajs.inputmaskData) {
		req.soajs.inputmaskData = {};
	}
	req.soajs.inputmaskData.isOwner = true;
	// req.soajs.inputmaskData.tCode = 'DBTN';

	mongoCore.findOne('tenants', { 'locked': true }, { applications: 0 }, function (error, tenant) {
		if (!error) {
			req.soajs.inputmaskData.tenantCode = tenant.code;
		}
		return cb(error, true);
	});
}

var libProduct = {
	"model": null,

	"guest": guest,

	"account": account,

	"admin": {
		/**
		 * List all the users and groups
		 * @param {Request Object} req
		 * @param {Callback Function} cb
		 */
		"listAll": function (req, cb) {
			checkIfTenantIsDBTN(req, function (error) {
				var data = {
					config: req.soajs.config,
					error: error,
					code: 405
				};
				utils.checkIfError(req, cb, data, false, function () {
					var combo = {
						collection: userCollectionName,
						condition: {},
						fields: {
							'password': 0, 'socialId': 0
						}
					};
					libProduct.model.initConnection(req.soajs);
					libProduct.model.findEntries(req.soajs, combo, function (err, userRecords) {
						data = {
							config: req.soajs.config,
							error: err,
							code: 405
						};
						utils.checkIfError(req, cb, data, false, function () {
							var combo = {
								collection: groupsCollectionName,
								condition: {}
							};
							libProduct.model.findEntries(req.soajs, combo, function (err, grpRecords) {
								libProduct.model.closeConnection(req.soajs);
								data.code = 415;
								data.error = err;
								utils.checkIfError(req, cb, data, false, function () {
									return cb(null, {
										'users': userRecords,
										'groups': grpRecords
									});
								});
							});
						});
					});
				});
			});
		},

		"user": {
			"countUsers": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.countUsers(req, cb);
					});
				});
			},

			"listUsers": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.listUsers(req, cb);
					});
				});
			},

			"getUser": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.getUser(req, cb);
					});
				});
			},

			"addUser": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.addUser(req, cb);
					});
				});
			},

			"editUser": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.editUser(req, cb);
					});
				});
			},

			"editConfig": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.editConfig(req, cb);
					});
				});
			},

			"changeStatus": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.changeStatus(req, cb);
					});
				});
			}
		},

		"group": {
			"list": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.list(req, cb);
					});
				});
			},

			"add": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.add(req, cb);
					});
				});
			},

			"edit": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.edit(req, cb);
					});
				});
			},

			"delete": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.delete(req, cb);
					});
				});
			},

			"addUsers": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.addUsers(req, cb);
					});
				});
			}
		},

		"tokens": token
	},

	"owner": {
		"user": user,

		"group": group,

		"tokens": token
	},

	"tenant": {

		"list": function (req, cb) {
			let config = req.soajs.config;

			var opts = {
				collection: "tenants"
			};

			if (req.soajs.inputmaskData.type) {
				if (Object.hasOwnProperty.call(req.soajs.inputmaskData, "negate") && req.soajs.inputmaskData.negate === true) {
					opts.conditions = { 'type': { $ne: req.soajs.inputmaskData.type } };
				}
				else {
					opts.conditions = { 'type': req.soajs.inputmaskData.type };
				}
			}

			opts.options = { "sort": { "name": 1 } };

			if (!mongoCore) {
				mongoCore = new Mongo(req.soajs.registry.coreDB.provision);
			}

			mongoCore.find(opts.collection, opts.conditions, opts.options, function (err, records) {
				utils.checkIfError(req, cb, { config: config, error: err, code: 436 }, false, function () {
					//generate oauth authorization if needed.
					records.forEach(function (oneTenant) {
						if (oneTenant.oauth && oneTenant.oauth.secret && oneTenant.oauth.secret !== '') {
							oneTenant.oauth.authorization = Auth.generate(oneTenant._id, oneTenant.oauth.secret);
						}
						else {
							oneTenant.oauth.authorization = "No Authorization Enabled, update tenant and set an 'oAuth Secret' to enable token generation.";
						}
					});
					return cb(null, records);
				});
			});
		},

		"getUserAclInfo": function (req, cb) {
			let config = req.soajs.config;
			let output = {};
			if (!mongoCore) {
				mongoCore = new Mongo(req.soajs.registry.coreDB.provision);
			}

			function listEnvironment(listEnvironmentCb) {
				let opts = {
					collection: "environment"
				};

				mongoCore.find(opts.collection, {locked: {$ne: true} }, function (err, records) {
					utils.checkIfError(req, listEnvironmentCb, {
						config: config,
						error: err,
						code: 437
					}, false, function () {
						return listEnvironmentCb(null, records);
					});
				});
			}

			function getTenant(getTenantCb) {

				function validateId(soajs, id, validateIdCb) {
					var id1;
					try {
						id1 = mongoCore.ObjectId(id);
						return validateIdCb(null, id1);
					}
					catch (e) {
						soajs.log.error(e);
						return validateIdCb({
							config: config,
							error: e,
							code: 426
						});
					}
				}

				var opts = {};
				validateId(req.soajs, req.soajs.inputmaskData.tenantId, function (err, tenantId) {
					utils.checkIfError(req, getTenantCb, { config: config, error: err, code: 438 }, false, function () {
						opts.collection = "tenants";
						opts.conditions = { "_id": tenantId };
						mongoCore.findOne(opts.collection, opts.conditions, function (err, data) {
							utils.checkIfError(req, getTenantCb, {
								config: config,
								error: err || !data,
								code: 438
							}, false, function () {
								return getTenantCb(null, data);
							});
						});
					});
				});
			}

			function listServices(listServicesCb) {
				var opts = {};
				opts.collection = "services";
				mongoCore.find(opts.collection, function (error, records) {
					if (error) {
						req.soajs.log.error(error);
						return listServicesCb({ code: 400, msg: error });
					}

					return listServicesCb(null, records);
				});
			}

			function getAndSetPackages(oneApplication, getPackagesCb) {
				var opts = {};
				opts.collection = "products";
				opts.conditions = { "code": oneApplication.product };
				mongoCore.findOne(opts.collection, opts.conditions, function (err, product) {
					utils.checkIfError(req, getPackagesCb, {
						config: config,
						error: err || !product,
						code: 460
					}, false, function () {
						var pck = {};
						var found = false;
						for (var i = 0; i < product.packages.length; i++) {
							if (product.packages[i].code === oneApplication.package) {
								pck = product.packages[i];
								found = true;
								break;
							}
						}

						utils.checkIfError(req, getPackagesCb, {
							config: config,
							error: !found,
							code: 461
						}, false, function () {
							oneApplication.parentPackageAcl = pck.acl;
							return getPackagesCb(null, pck);
						});
					});
				});
			}

			async.parallel([
					listEnvironment,
					getTenant,
					listServices
				],
				function (asyncParallelError, asyncParallelResults) {
					if (asyncParallelError) {
						return cb(asyncParallelError);
					}

					output.environment = asyncParallelResults[0];
					output.tenant = asyncParallelResults[1];
					output.services = asyncParallelResults[2];

					let applications = output.tenant.applications;
					async.each(applications, function (oneApplication, asyncEachCb) {
						if (!Object.hasOwnProperty.call(oneApplication, 'acl')) {
							getAndSetPackages(oneApplication, asyncEachCb);
						} else {
							return asyncEachCb();
						}
					}, function (asyncError) {
						if (asyncError) {
							req.soajs.log.error(asyncError);
							return cb(asyncError);
						} else {
							return cb(null, output);
						}
					});
				});
		}
	}
};

module.exports = {
	"clearMongo": function () { // added for unit test cases only
		mongoCore = null;
	},

	"init": function (modelName, cb) {
		var modelPath = __dirname + "/../model/" + modelName + ".js";
		return requireModel(modelPath, cb);

		/**
		 * checks if model file exists, requires it and returns it.
		 * @param filePath
		 * @param cb
		 */
		function requireModel(filePath, cb) {
			//check if file exist. if not return error
			fs.exists(filePath, function (exists) {
				if (!exists) {
					return cb(new Error("Requested Model Not Found!"));
				}

				account.model = require(filePath);
				guest.model = require(filePath);
				user.model = require(filePath);
				group.model = require(filePath);
				token.model = require(filePath);

				libProduct.model = require(filePath);
				return cb(null, libProduct);
			});
		}
	}
};<commit_msg>added assertion<commit_after>'use strict';
var fs = require("fs");
var uuid = require('uuid');
var userCollectionName = "users";
var groupsCollectionName = "groups";

var guest = require("./guest.js");
var account = require("./account.js");
var user = require("./user.js");
var group = require("./group.js");
var token = require("./token.js");

var utils = require("./utils.js");

var Mongo = require("soajs").mongo;
var mongoCore = null;

var soajs = require("soajs");
var Auth = soajs.authorization;

var async = require('async');

function checkIfTenantIsDBTN(req, cb) {
	if (!mongoCore) {
		mongoCore = new Mongo(req.soajs.registry.coreDB.provision);
	}

	if (!req.soajs.inputmaskData) {
		req.soajs.inputmaskData = {};
	}
	req.soajs.inputmaskData.isOwner = true;
	// req.soajs.inputmaskData.tCode = 'DBTN';

	mongoCore.findOne('tenants', { 'locked': true }, { applications: 0 }, function (error, tenant) {
		if (!error && tenant) {
			req.soajs.inputmaskData.tenantCode = tenant.code;
		}
		return cb(error, true);
	});
}

var libProduct = {
	"model": null,

	"guest": guest,

	"account": account,

	"admin": {
		/**
		 * List all the users and groups
		 * @param {Request Object} req
		 * @param {Callback Function} cb
		 */
		"listAll": function (req, cb) {
			checkIfTenantIsDBTN(req, function (error) {
				var data = {
					config: req.soajs.config,
					error: error,
					code: 405
				};
				utils.checkIfError(req, cb, data, false, function () {
					var combo = {
						collection: userCollectionName,
						condition: {},
						fields: {
							'password': 0, 'socialId': 0
						}
					};
					libProduct.model.initConnection(req.soajs);
					libProduct.model.findEntries(req.soajs, combo, function (err, userRecords) {
						data = {
							config: req.soajs.config,
							error: err,
							code: 405
						};
						utils.checkIfError(req, cb, data, false, function () {
							var combo = {
								collection: groupsCollectionName,
								condition: {}
							};
							libProduct.model.findEntries(req.soajs, combo, function (err, grpRecords) {
								libProduct.model.closeConnection(req.soajs);
								data.code = 415;
								data.error = err;
								utils.checkIfError(req, cb, data, false, function () {
									return cb(null, {
										'users': userRecords,
										'groups': grpRecords
									});
								});
							});
						});
					});
				});
			});
		},

		"user": {
			"countUsers": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.countUsers(req, cb);
					});
				});
			},

			"listUsers": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.listUsers(req, cb);
					});
				});
			},

			"getUser": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.getUser(req, cb);
					});
				});
			},

			"addUser": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.addUser(req, cb);
					});
				});
			},

			"editUser": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.editUser(req, cb);
					});
				});
			},

			"editConfig": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.editConfig(req, cb);
					});
				});
			},

			"changeStatus": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						user.changeStatus(req, cb);
					});
				});
			}
		},

		"group": {
			"list": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.list(req, cb);
					});
				});
			},

			"add": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.add(req, cb);
					});
				});
			},

			"edit": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.edit(req, cb);
					});
				});
			},

			"delete": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.delete(req, cb);
					});
				});
			},

			"addUsers": function (req, cb) {
				checkIfTenantIsDBTN(req, function (error) {
					var data = {
						config: req.soajs.config,
						error: error,
						code: 405
					};
					utils.checkIfError(req, cb, data, false, function () {
						group.addUsers(req, cb);
					});
				});
			}
		},

		"tokens": token
	},

	"owner": {
		"user": user,

		"group": group,

		"tokens": token
	},

	"tenant": {

		"list": function (req, cb) {
			let config = req.soajs.config;

			var opts = {
				collection: "tenants"
			};

			if (req.soajs.inputmaskData.type) {
				if (Object.hasOwnProperty.call(req.soajs.inputmaskData, "negate") && req.soajs.inputmaskData.negate === true) {
					opts.conditions = { 'type': { $ne: req.soajs.inputmaskData.type } };
				}
				else {
					opts.conditions = { 'type': req.soajs.inputmaskData.type };
				}
			}

			opts.options = { "sort": { "name": 1 } };

			if (!mongoCore) {
				mongoCore = new Mongo(req.soajs.registry.coreDB.provision);
			}

			mongoCore.find(opts.collection, opts.conditions, opts.options, function (err, records) {
				utils.checkIfError(req, cb, { config: config, error: err, code: 436 }, false, function () {
					//generate oauth authorization if needed.
					records.forEach(function (oneTenant) {
						if (oneTenant.oauth && oneTenant.oauth.secret && oneTenant.oauth.secret !== '') {
							oneTenant.oauth.authorization = Auth.generate(oneTenant._id, oneTenant.oauth.secret);
						}
						else {
							oneTenant.oauth.authorization = "No Authorization Enabled, update tenant and set an 'oAuth Secret' to enable token generation.";
						}
					});
					return cb(null, records);
				});
			});
		},

		"getUserAclInfo": function (req, cb) {
			let config = req.soajs.config;
			let output = {};
			if (!mongoCore) {
				mongoCore = new Mongo(req.soajs.registry.coreDB.provision);
			}

			function listEnvironment(listEnvironmentCb) {
				let opts = {
					collection: "environment"
				};

				mongoCore.find(opts.collection, {locked: {$ne: true} }, function (err, records) {
					utils.checkIfError(req, listEnvironmentCb, {
						config: config,
						error: err,
						code: 437
					}, false, function () {
						return listEnvironmentCb(null, records);
					});
				});
			}

			function getTenant(getTenantCb) {

				function validateId(soajs, id, validateIdCb) {
					var id1;
					try {
						id1 = mongoCore.ObjectId(id);
						return validateIdCb(null, id1);
					}
					catch (e) {
						soajs.log.error(e);
						return validateIdCb({
							config: config,
							error: e,
							code: 426
						});
					}
				}

				var opts = {};
				validateId(req.soajs, req.soajs.inputmaskData.tenantId, function (err, tenantId) {
					utils.checkIfError(req, getTenantCb, { config: config, error: err, code: 438 }, false, function () {
						opts.collection = "tenants";
						opts.conditions = { "_id": tenantId };
						mongoCore.findOne(opts.collection, opts.conditions, function (err, data) {
							utils.checkIfError(req, getTenantCb, {
								config: config,
								error: err || !data,
								code: 438
							}, false, function () {
								return getTenantCb(null, data);
							});
						});
					});
				});
			}

			function listServices(listServicesCb) {
				var opts = {};
				opts.collection = "services";
				mongoCore.find(opts.collection, function (error, records) {
					if (error) {
						req.soajs.log.error(error);
						return listServicesCb({ code: 400, msg: error });
					}

					return listServicesCb(null, records);
				});
			}

			function getAndSetPackages(oneApplication, getPackagesCb) {
				var opts = {};
				opts.collection = "products";
				opts.conditions = { "code": oneApplication.product };
				mongoCore.findOne(opts.collection, opts.conditions, function (err, product) {
					utils.checkIfError(req, getPackagesCb, {
						config: config,
						error: err || !product,
						code: 460
					}, false, function () {
						var pck = {};
						var found = false;
						for (var i = 0; i < product.packages.length; i++) {
							if (product.packages[i].code === oneApplication.package) {
								pck = product.packages[i];
								found = true;
								break;
							}
						}

						utils.checkIfError(req, getPackagesCb, {
							config: config,
							error: !found,
							code: 461
						}, false, function () {
							oneApplication.parentPackageAcl = pck.acl;
							return getPackagesCb(null, pck);
						});
					});
				});
			}

			async.parallel([
					listEnvironment,
					getTenant,
					listServices
				],
				function (asyncParallelError, asyncParallelResults) {
					if (asyncParallelError) {
						return cb(asyncParallelError);
					}

					output.environment = asyncParallelResults[0];
					output.tenant = asyncParallelResults[1];
					output.services = asyncParallelResults[2];

					let applications = output.tenant.applications;
					async.each(applications, function (oneApplication, asyncEachCb) {
						if (!Object.hasOwnProperty.call(oneApplication, 'acl')) {
							getAndSetPackages(oneApplication, asyncEachCb);
						} else {
							return asyncEachCb();
						}
					}, function (asyncError) {
						if (asyncError) {
							req.soajs.log.error(asyncError);
							return cb(asyncError);
						} else {
							return cb(null, output);
						}
					});
				});
		}
	}
};

module.exports = {
	"clearMongo": function () { // added for unit test cases only
		mongoCore = null;
	},

	"init": function (modelName, cb) {
		var modelPath = __dirname + "/../model/" + modelName + ".js";
		return requireModel(modelPath, cb);

		/**
		 * checks if model file exists, requires it and returns it.
		 * @param filePath
		 * @param cb
		 */
		function requireModel(filePath, cb) {
			//check if file exist. if not return error
			fs.exists(filePath, function (exists) {
				if (!exists) {
					return cb(new Error("Requested Model Not Found!"));
				}

				account.model = require(filePath);
				guest.model = require(filePath);
				user.model = require(filePath);
				group.model = require(filePath);
				token.model = require(filePath);

				libProduct.model = require(filePath);
				return cb(null, libProduct);
			});
		}
	}
};<|endoftext|>


<commit_before>// @flow

const semver = require(`semver`)
const { isCI } = require(`ci-info`)
const signalExit = require(`signal-exit`)
const reporterActions = require(`./redux/actions`)

const { LogLevels, ActivityStatuses, ActivityTypes } = require(`./constants`)

let inkExists = false
try {
  inkExists = require.resolve(`ink`)
  // eslint-disable-next-line no-empty
} catch (err) {}

if (!process.env.GATSBY_LOGGER) {
  if (
    inkExists &&
    semver.satisfies(process.version, `>=8`) &&
    !isCI &&
    typeof jest === `undefined`
  ) {
    process.env.GATSBY_LOGGER = `ink`
  } else {
    process.env.GATSBY_LOGGER = `yurnalist`
  }
}
// if child process - use ipc logger
if (process.send) {
  // process.env.FORCE_COLOR = `0`

  require(`./loggers/ipc`)
}

if (process.env.GATSBY_LOGGER.includes(`json`)) {
  require(`./loggers/json`)
} else if (process.env.GATSBY_LOGGER.includes(`yurnalist`)) {
  require(`./loggers/yurnalist`)
} else {
  require(`./loggers/ink`)
}

const util = require(`util`)
const { stripIndent } = require(`common-tags`)
const chalk = require(`chalk`)
const { trackError } = require(`gatsby-telemetry`)
const tracer = require(`opentracing`).globalTracer()

const { getErrorFormatter } = require(`./errors`)
const { getStore } = require(`./redux`)
const constructError = require(`../structured-errors/construct-error`)

const errorFormatter = getErrorFormatter()

import type { ActivityTracker, ActivityArgs, Reporter } from "./types"

const addMessage = level => text => reporterActions.createLog({ level, text })

let isVerbose = false

const interuptActivities = () => {
  const { activities } = getStore().getState().logs
  Object.keys(activities).forEach(activityId => {
    const activity = activities[activityId]
    if (
      activity.status === ActivityStatuses.InProgress ||
      activity.status === ActivityStatuses.NotStarted
    ) {
      reporter.completeActivity(activityId, ActivityStatuses.Interrupted)
    }
  })
}

const prematureEnd = () => {
  // hack so at least one activity is surely failed, so
  // we are guaranteed to generate FAILED status
  // if none of activity did explicitly fail
  reporterActions.createPendingActivity({
    id: `panic`,
    status: ActivityStatuses.Failed,
  })

  interuptActivities()
}

signalExit((code, signal) => {
  if (code !== 0 && signal !== `SIGINT` && signal !== `SIGTERM`) prematureEnd()
  else interuptActivities()
})

/**
 * Reporter module.
 * @module reporter
 */
const reporter: Reporter = {
  /**
   * Strip initial indentation template function.
   */
  stripIndent,
  format: chalk,
  /**
   * Toggle verbosity.
   * @param {boolean} [isVerbose=true]
   */
  setVerbose: (_isVerbose = true) => {
    isVerbose = _isVerbose
  },
  /**
   * Turn off colors in error output.
   * @param {boolean} [isNoColor=false]
   */
  setNoColor(isNoColor = false) {
    if (isNoColor) {
      errorFormatter.withoutColors()
    }

    // disables colors in popular terminal output coloring packages
    //  - chalk: see https://www.npmjs.com/package/chalk#chalksupportscolor
    //  - ansi-colors: see https://github.com/doowb/ansi-colors/blob/8024126c7115a0efb25a9a0e87bc5e29fd66831f/index.js#L5-L7
    if (isNoColor) {
      process.env.FORCE_COLOR = `0`
    }
  },
  /**
   * Log arguments and exit process with status 1.
   * @param {*} args
   */
  panic(...args) {
    const error = reporter.error(...args)
    trackError(`GENERAL_PANIC`, { error })
    prematureEnd()
    process.exit(1)
  },

  panicOnBuild(...args) {
    const error = reporter.error(...args)
    trackError(`BUILD_PANIC`, { error })
    if (process.env.gatsby_executing_command === `build`) {
      prematureEnd()
      process.exit(1)
    }
    return error
  },

  error(errorMeta, error) {
    let details = {}
    // Many paths to retain backcompat :scream:
    if (arguments.length === 2) {
      if (Array.isArray(error)) {
        return error.map(errorItem => this.error(errorMeta, errorItem))
      }
      details.error = error
      details.context = {
        sourceMessage: errorMeta + ` ` + error.message,
      }
    } else if (arguments.length === 1 && errorMeta instanceof Error) {
      details.error = errorMeta
      details.context = {
        sourceMessage: errorMeta.message,
      }
    } else if (arguments.length === 1 && Array.isArray(errorMeta)) {
      // when we get an array of messages, call this function once for each error
      return errorMeta.map(errorItem => this.error(errorItem))
    } else if (arguments.length === 1 && typeof errorMeta === `object`) {
      details = Object.assign({}, errorMeta)
    } else if (arguments.length === 1 && typeof errorMeta === `string`) {
      details.context = {
        sourceMessage: errorMeta,
      }
    }

    const structuredError = constructError({ details })
    if (structuredError) {
      reporterActions.createLog(structuredError)
    }

    // TODO: remove this once Error component can render this info
    // log formatted stacktrace
    if (structuredError.error) {
      this.log(errorFormatter.render(structuredError.error))
    }
    return structuredError
  },

  /**
   * Set prefix on uptime.
   * @param {string} prefix - A string to prefix uptime with.
   */
  uptime(prefix) {
    this.verbose(`${prefix}: ${(process.uptime() * 1000).toFixed(3)}ms`)
  },

  verbose: text => {
    if (isVerbose) {
      reporterActions.createLog({
        level: LogLevels.Debug,
        text,
      })
    }
  },

  success: addMessage(LogLevels.Success),
  info: addMessage(LogLevels.Info),
  warn: addMessage(LogLevels.Warning),
  log: addMessage(LogLevels.Log),

  pendingActivity: reporterActions.createPendingActivity,

  completeActivity: (id: string, status: string = ActivityStatuses.Success) => {
    reporterActions.endActivity({ id, status })
  },

  /**
   * Time an activity.
   * @param {string} name - Name of activity.
   * @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
   * @returns {ActivityTracker} The activity tracker.
   */
  activityTimer(
    text: string,
    activityArgs: ActivityArgs = {}
  ): ActivityTracker {
    let { parentSpan, id } = activityArgs
    const spanArgs = parentSpan ? { childOf: parentSpan } : {}
    if (!id) {
      id = text
    }

    const span = tracer.startSpan(text, spanArgs)

    return {
      start: () => {
        reporterActions.startActivity({
          id,
          text,
          type: ActivityTypes.Spinner,
        })
      },
      setStatus: statusText => {
        reporterActions.setActivityStatusText({
          id,
          statusText,
        })
      },
      panicOnBuild(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panicOnBuild(...args)
      },
      panic(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panic(...args)
      },
      end() {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Success,
        })
      },
      span,
    }
  },

  /**
   * Create an Activity that is not visible to the user
   *
   * During the lifecycle of the Gatsby process, sometimes we need to do some
   * async work and wait for it to complete. A typical example of this is a job.
   * This work should set the status of the process to `in progress` while running and
   * `complete` (or `failure`) when complete. Activities do just this! However, they
   * are visible to the user. So this function can be used to create a _hidden_ activity
   * that while not displayed in the CLI, still triggers a change in process status.
   *
   * @param {string} name - Name of activity.
   * @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
   * @returns {ActivityTracker} The activity tracker.
   */
  phantomActivity(
    text: string,
    activityArgs: ActivityArgs = {}
  ): ActivityTracker {
    let { parentSpan, id } = activityArgs
    const spanArgs = parentSpan ? { childOf: parentSpan } : {}
    if (!id) {
      id = text
    }

    const span = tracer.startSpan(text, spanArgs)

    return {
      start: () => {
        reporterActions.startActivity({
          id,
          text,
          type: ActivityTypes.Hidden,
        })
      },
      end() {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Success,
        })
      },
      span,
    }
  },

  /**
   * Create a progress bar for an activity
   * @param {string} name - Name of activity.
   * @param {number} total - Total items to be processed.
   * @param {number} start - Start count to show.
   * @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
   * @returns {ActivityTracker} The activity tracker.
   */
  createProgress(
    text: string,
    total = 0,
    start = 0,
    activityArgs: ActivityArgs = {}
  ): ActivityTracker {
    let { parentSpan, id } = activityArgs
    const spanArgs = parentSpan ? { childOf: parentSpan } : {}
    if (!id) {
      id = text
    }
    const span = tracer.startSpan(text, spanArgs)

    return {
      start: () => {
        reporterActions.startActivity({
          id,
          text,
          type: ActivityTypes.Progress,
          current: start,
          total,
        })
      },
      setStatus: statusText => {
        reporterActions.setActivityStatusText({
          id,
          statusText,
        })
      },
      tick: (increment = 1) => {
        reporterActions.activityTick({ id, increment })
      },
      panicOnBuild(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panicOnBuild(...args)
      },
      panic(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panic(...args)
      },
      done: () => {
        span.finish()
        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Success,
        })
      },
      set total(value) {
        reporterActions.setActivityTotal({ id, total: value })
      },
      span,
    }
  },
}

console.log = (...args) => reporter.log(util.format(...args))
console.warn = (...args) => reporter.warn(util.format(...args))
console.info = (...args) => reporter.info(util.format(...args))
console.error = (...args) => reporter.error(util.format(...args))

module.exports = reporter
<commit_msg>fix(gatsby-cli): re-add reporter._setStage as no-op function (#18797)<commit_after>// @flow

const semver = require(`semver`)
const { isCI } = require(`ci-info`)
const signalExit = require(`signal-exit`)
const reporterActions = require(`./redux/actions`)

const { LogLevels, ActivityStatuses, ActivityTypes } = require(`./constants`)

let inkExists = false
try {
  inkExists = require.resolve(`ink`)
  // eslint-disable-next-line no-empty
} catch (err) {}

if (!process.env.GATSBY_LOGGER) {
  if (
    inkExists &&
    semver.satisfies(process.version, `>=8`) &&
    !isCI &&
    typeof jest === `undefined`
  ) {
    process.env.GATSBY_LOGGER = `ink`
  } else {
    process.env.GATSBY_LOGGER = `yurnalist`
  }
}
// if child process - use ipc logger
if (process.send) {
  // process.env.FORCE_COLOR = `0`

  require(`./loggers/ipc`)
}

if (process.env.GATSBY_LOGGER.includes(`json`)) {
  require(`./loggers/json`)
} else if (process.env.GATSBY_LOGGER.includes(`yurnalist`)) {
  require(`./loggers/yurnalist`)
} else {
  require(`./loggers/ink`)
}

const util = require(`util`)
const { stripIndent } = require(`common-tags`)
const chalk = require(`chalk`)
const { trackError } = require(`gatsby-telemetry`)
const tracer = require(`opentracing`).globalTracer()

const { getErrorFormatter } = require(`./errors`)
const { getStore } = require(`./redux`)
const constructError = require(`../structured-errors/construct-error`)

const errorFormatter = getErrorFormatter()

import type { ActivityTracker, ActivityArgs, Reporter } from "./types"

const addMessage = level => text => reporterActions.createLog({ level, text })

let isVerbose = false

const interuptActivities = () => {
  const { activities } = getStore().getState().logs
  Object.keys(activities).forEach(activityId => {
    const activity = activities[activityId]
    if (
      activity.status === ActivityStatuses.InProgress ||
      activity.status === ActivityStatuses.NotStarted
    ) {
      reporter.completeActivity(activityId, ActivityStatuses.Interrupted)
    }
  })
}

const prematureEnd = () => {
  // hack so at least one activity is surely failed, so
  // we are guaranteed to generate FAILED status
  // if none of activity did explicitly fail
  reporterActions.createPendingActivity({
    id: `panic`,
    status: ActivityStatuses.Failed,
  })

  interuptActivities()
}

signalExit((code, signal) => {
  if (code !== 0 && signal !== `SIGINT` && signal !== `SIGTERM`) prematureEnd()
  else interuptActivities()
})

/**
 * Reporter module.
 * @module reporter
 */
const reporter: Reporter = {
  /**
   * Strip initial indentation template function.
   */
  stripIndent,
  format: chalk,
  /**
   * Toggle verbosity.
   * @param {boolean} [isVerbose=true]
   */
  setVerbose: (_isVerbose = true) => {
    isVerbose = _isVerbose
  },
  /**
   * Turn off colors in error output.
   * @param {boolean} [isNoColor=false]
   */
  setNoColor(isNoColor = false) {
    if (isNoColor) {
      errorFormatter.withoutColors()
    }

    // disables colors in popular terminal output coloring packages
    //  - chalk: see https://www.npmjs.com/package/chalk#chalksupportscolor
    //  - ansi-colors: see https://github.com/doowb/ansi-colors/blob/8024126c7115a0efb25a9a0e87bc5e29fd66831f/index.js#L5-L7
    if (isNoColor) {
      process.env.FORCE_COLOR = `0`
    }
  },
  /**
   * Log arguments and exit process with status 1.
   * @param {*} args
   */
  panic(...args) {
    const error = reporter.error(...args)
    trackError(`GENERAL_PANIC`, { error })
    prematureEnd()
    process.exit(1)
  },

  panicOnBuild(...args) {
    const error = reporter.error(...args)
    trackError(`BUILD_PANIC`, { error })
    if (process.env.gatsby_executing_command === `build`) {
      prematureEnd()
      process.exit(1)
    }
    return error
  },

  error(errorMeta, error) {
    let details = {}
    // Many paths to retain backcompat :scream:
    if (arguments.length === 2) {
      if (Array.isArray(error)) {
        return error.map(errorItem => this.error(errorMeta, errorItem))
      }
      details.error = error
      details.context = {
        sourceMessage: errorMeta + ` ` + error.message,
      }
    } else if (arguments.length === 1 && errorMeta instanceof Error) {
      details.error = errorMeta
      details.context = {
        sourceMessage: errorMeta.message,
      }
    } else if (arguments.length === 1 && Array.isArray(errorMeta)) {
      // when we get an array of messages, call this function once for each error
      return errorMeta.map(errorItem => this.error(errorItem))
    } else if (arguments.length === 1 && typeof errorMeta === `object`) {
      details = Object.assign({}, errorMeta)
    } else if (arguments.length === 1 && typeof errorMeta === `string`) {
      details.context = {
        sourceMessage: errorMeta,
      }
    }

    const structuredError = constructError({ details })
    if (structuredError) {
      reporterActions.createLog(structuredError)
    }

    // TODO: remove this once Error component can render this info
    // log formatted stacktrace
    if (structuredError.error) {
      this.log(errorFormatter.render(structuredError.error))
    }
    return structuredError
  },

  /**
   * Set prefix on uptime.
   * @param {string} prefix - A string to prefix uptime with.
   */
  uptime(prefix) {
    this.verbose(`${prefix}: ${(process.uptime() * 1000).toFixed(3)}ms`)
  },

  verbose: text => {
    if (isVerbose) {
      reporterActions.createLog({
        level: LogLevels.Debug,
        text,
      })
    }
  },

  success: addMessage(LogLevels.Success),
  info: addMessage(LogLevels.Info),
  warn: addMessage(LogLevels.Warning),
  log: addMessage(LogLevels.Log),

  pendingActivity: reporterActions.createPendingActivity,

  completeActivity: (id: string, status: string = ActivityStatuses.Success) => {
    reporterActions.endActivity({ id, status })
  },

  /**
   * Time an activity.
   * @param {string} name - Name of activity.
   * @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
   * @returns {ActivityTracker} The activity tracker.
   */
  activityTimer(
    text: string,
    activityArgs: ActivityArgs = {}
  ): ActivityTracker {
    let { parentSpan, id } = activityArgs
    const spanArgs = parentSpan ? { childOf: parentSpan } : {}
    if (!id) {
      id = text
    }

    const span = tracer.startSpan(text, spanArgs)

    return {
      start: () => {
        reporterActions.startActivity({
          id,
          text,
          type: ActivityTypes.Spinner,
        })
      },
      setStatus: statusText => {
        reporterActions.setActivityStatusText({
          id,
          statusText,
        })
      },
      panicOnBuild(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panicOnBuild(...args)
      },
      panic(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panic(...args)
      },
      end() {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Success,
        })
      },
      span,
    }
  },

  /**
   * Create an Activity that is not visible to the user
   *
   * During the lifecycle of the Gatsby process, sometimes we need to do some
   * async work and wait for it to complete. A typical example of this is a job.
   * This work should set the status of the process to `in progress` while running and
   * `complete` (or `failure`) when complete. Activities do just this! However, they
   * are visible to the user. So this function can be used to create a _hidden_ activity
   * that while not displayed in the CLI, still triggers a change in process status.
   *
   * @param {string} name - Name of activity.
   * @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
   * @returns {ActivityTracker} The activity tracker.
   */
  phantomActivity(
    text: string,
    activityArgs: ActivityArgs = {}
  ): ActivityTracker {
    let { parentSpan, id } = activityArgs
    const spanArgs = parentSpan ? { childOf: parentSpan } : {}
    if (!id) {
      id = text
    }

    const span = tracer.startSpan(text, spanArgs)

    return {
      start: () => {
        reporterActions.startActivity({
          id,
          text,
          type: ActivityTypes.Hidden,
        })
      },
      end() {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Success,
        })
      },
      span,
    }
  },

  /**
   * Create a progress bar for an activity
   * @param {string} name - Name of activity.
   * @param {number} total - Total items to be processed.
   * @param {number} start - Start count to show.
   * @param {ActivityArgs} activityArgs - optional object with tracer parentSpan
   * @returns {ActivityTracker} The activity tracker.
   */
  createProgress(
    text: string,
    total = 0,
    start = 0,
    activityArgs: ActivityArgs = {}
  ): ActivityTracker {
    let { parentSpan, id } = activityArgs
    const spanArgs = parentSpan ? { childOf: parentSpan } : {}
    if (!id) {
      id = text
    }
    const span = tracer.startSpan(text, spanArgs)

    return {
      start: () => {
        reporterActions.startActivity({
          id,
          text,
          type: ActivityTypes.Progress,
          current: start,
          total,
        })
      },
      setStatus: statusText => {
        reporterActions.setActivityStatusText({
          id,
          statusText,
        })
      },
      tick: (increment = 1) => {
        reporterActions.activityTick({ id, increment })
      },
      panicOnBuild(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panicOnBuild(...args)
      },
      panic(...args) {
        span.finish()

        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Failed,
        })

        return reporter.panic(...args)
      },
      done: () => {
        span.finish()
        reporterActions.endActivity({
          id,
          status: ActivityStatuses.Success,
        })
      },
      set total(value) {
        reporterActions.setActivityTotal({ id, total: value })
      },
      span,
    }
  },
  // This method was called in older versions of gatsby, so we need to keep it to avoid
  // "reporter._setStage is not a function" error when gatsby@<2.16 is used with gatsby-cli@>=2.8
  _setStage() {},
}

console.log = (...args) => reporter.log(util.format(...args))
console.warn = (...args) => reporter.warn(util.format(...args))
console.info = (...args) => reporter.info(util.format(...args))
console.error = (...args) => reporter.error(util.format(...args))

module.exports = reporter
<|endoftext|>


<commit_before>describe('core', () => {

    beforeEach(() => {
        browser.url('about:blank');
    });

    afterEach(() => {
        browser.sessionStorage('DELETE');
    });

    // Initialisation

    it('uses List mode in lack of any state', () => {
        browser.url('/core/none.html');
        browser.isExisting('.shower.list').should.equal(true);
    });

    it('stays in List mode if `list` state is present', () => {
        browser.url('/core/list.html');
        browser.isExisting('.shower.list').should.equal(true);
    });

    it('stays in Full mode if `full` state is present', () => {
        browser.url('/core/full.html');
        browser.isExisting('.shower.full').should.equal(true);
    });

    it('keeps Full mode after reload', () => {
        browser.url('/core/full.html');
        browser.refresh();
        browser.isExisting('.shower.full').should.equal(true);
    });

    it('adds ID for all slides if it’s not alredy set', () => {
        browser.url('/core/list.html');
        browser.isExisting('[id="1"]').should.equal(true);
        browser.isExisting('[id="2"]').should.equal(false);
        browser.isExisting('[id="ID"]').should.equal(true);
        browser.isExisting('[id="3"]').should.equal(true);
    });

    it('doesn’t set any slide states on initialisation', () => {
        browser.url('/core/list.html');
        browser.isExisting('.active').should.equal(false);
        browser.isExisting('.visited').should.equal(false);
    });

    it('doesn’t set any `visited` slide states and activates only current slide', () => {
        browser.url('/core/list.html#ID');
        browser.isExisting('[id="1"]:not(.active):not(.visited)').should.equal(true);
        browser.isExisting('[id="ID"].active:not(.visited)').should.equal(true);
        browser.isExisting('[id="3"]:not(.active):not(.visited)').should.equal(true);
    });

    // URL

    it('activates a slide if its ID is present in URL', () => {
        browser.url('/core/list.html#ID');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('changes non-existing ID to ID of the first slide in URL', () => {
        browser.url('/core/list.html#404');
        browser.getUrl().should.match(/#1/, 'URL in Full mode')
    });

    it('activates the first slide if ID in URL doesn’t exist', () => {
        browser.url('/core/list.html#404');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    // Action

    it('activates the first slide in the List mode if Right Arrow key is pressed', () => {
        browser.url('/core/list.html');
        browser.keys('\uE014'); // Right
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('goes to Full mode when a slide is clicked', () => {
        browser.url('/core/list.html');
        browser.click('[id="1"]');
        browser.isExisting('.shower.full').should.equal(true);
    });

    // Traverse

    it('changes slide states while moving forward', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.keys('\uE014'); // Right
        browser.isExisting('[id="1"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="ID"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="3"].active').should.equal(true);
    });

    it('changes slide states while moving backward', () => {
        browser.url('/core/list.html#3');
        browser.keys('\uE012'); // Left
        browser.keys('\uE012'); // Left
        browser.isExisting('[id="1"].active').should.equal(true);
        browser.isExisting('[id="ID"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="3"].visited:not(.active)').should.equal(true);
    });

    it('changes slide states while moving forward and backward', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.keys('\uE014'); // Right
        browser.keys('\uE012'); // Left
        browser.keys('\uE012'); // Left
        browser.isExisting('[id="1"].visited.active').should.equal(true);
        browser.isExisting('[id="ID"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="3"].visited:not(.active)').should.equal(true);
    });

    // History

    it('goes to previous and next slides while moving back and forward in history', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.keys('\uE014'); // Right
        browser.back();
        browser.back();
        browser.forward();
        browser.isExisting('[id="1"].visited').should.equal(true);
        browser.isExisting('[id="ID"].visited.active').should.equal(true);
        browser.isExisting('[id="3"].visited').should.equal(true);
    });

    // Forward

    it('moves forward when Right Arrow key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Down Arrow key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE015'); // Down
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Page Down key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE00F'); // Page Down
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when J key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('J');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when L key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('L');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Enter key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('Enter'); //
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Space key is pressed in Full mode', () => {
        browser.url('/core/list.html');
        browser.click('[id="1"]');
        browser.keys('Space');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('doesn’t move forward when Space key is pressed in List mode', () => {
        browser.url('/core/list.html#1');
        browser.keys('Space');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    // Backward

    it('moves backward when Left Arrow key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('\uE012'); // Left
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Up Arrow key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('\uE013'); // Up
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Page Up key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('\uE00E'); // Page Up
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when K key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('K');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when H key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('H');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Shift Enter keys are pressed in List mode', () => {
        browser.url('/core/list.html#ID');
        browser.keys(['Shift', 'Enter']);
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Shift Enter keys are pressed in Full mode', () => {
        browser.url('/core/list.html');
        browser.click('[id="ID"]');
        browser.keys(['Shift', 'Enter']);
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('doesn’t move backward when Shift Space keys are pressed in List mode', () => {
        browser.url('/core/list.html#ID')
        browser.keys(['Shift', 'Space']);
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves backward when Shift Space keys are pressed in Full mode', () => {
        browser.url('/core/list.html');
        browser.click('[id="ID"]');
        browser.keys(['Shift', 'Space']);
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('goes to first slide when Home key is pressed', () => {
        browser.url('/core/list.html#3');
        browser.keys('Home');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('goes to last slide when End key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('End');
        browser.isExisting('[id="3"].active').should.equal(true);
    });

    // F5
    // Shift F5

    // Cmd Enter
    // Cmd Shift Enter
    // Cmd Shift Enter

    // Esc

    // Title

    it('keeps page title unchanged in List mode', () => {
        browser.url('/core/title.html');
        browser.getTitle().should.equal('Title');
    });

    it('prepends page title with current slide title in Full mode', () => {
        browser.url('/core/title.html');
        browser.click('[id="1"]');
        browser.getTitle().should.equal('1 — Title')
    });

    it('doesn’t change page title in lack of slide title', () => {
        browser.url('/core/title.html');
        browser.click('[id="2"]');
        browser.getTitle().should.equal('Title');
    });

    it('strips tags from slide title while prepending', () => {
        browser.url('/core/title.html');
        browser.click('[id="3"]');
        browser.getTitle().should.equal('3 — Title');
    });

});
<commit_msg>All shortcuts covered, broken marked with x<commit_after>describe('core', () => {

    beforeEach(() => {
        browser.url('about:blank');
    });

    afterEach(() => {
        browser.sessionStorage('DELETE');
    });

    // Initialisation

    it('uses List mode in lack of any state', () => {
        browser.url('/core/none.html');
        browser.isExisting('.shower.list').should.equal(true);
    });

    it('stays in List mode if `list` state is present', () => {
        browser.url('/core/list.html');
        browser.isExisting('.shower.list').should.equal(true);
    });

    it('stays in Full mode if `full` state is present', () => {
        browser.url('/core/full.html');
        browser.isExisting('.shower.full').should.equal(true);
    });

    it('keeps Full mode after reload', () => {
        browser.url('/core/full.html');
        browser.refresh();
        browser.isExisting('.shower.full').should.equal(true);
    });

    it('adds ID for all slides if it’s not alredy set', () => {
        browser.url('/core/list.html');
        browser.isExisting('[id="1"]').should.equal(true);
        browser.isExisting('[id="2"]').should.equal(false);
        browser.isExisting('[id="ID"]').should.equal(true);
        browser.isExisting('[id="3"]').should.equal(true);
    });

    it('doesn’t set any slide states on initialisation', () => {
        browser.url('/core/list.html');
        browser.isExisting('.active').should.equal(false);
        browser.isExisting('.visited').should.equal(false);
    });

    it('doesn’t set any `visited` slide states and activates only current slide', () => {
        browser.url('/core/list.html#ID');
        browser.isExisting('[id="1"]:not(.active):not(.visited)').should.equal(true);
        browser.isExisting('[id="ID"].active:not(.visited)').should.equal(true);
        browser.isExisting('[id="3"]:not(.active):not(.visited)').should.equal(true);
    });

    // URL

    it('activates a slide if its ID is present in URL', () => {
        browser.url('/core/list.html#ID');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('changes non-existing ID to ID of the first slide in URL', () => {
        browser.url('/core/list.html#404');
        browser.getUrl().should.match(/#1/, 'URL in Full mode')
    });

    it('activates the first slide if ID in URL doesn’t exist', () => {
        browser.url('/core/list.html#404');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    // Action

    it('activates the first slide in the List mode if Right Arrow key is pressed', () => {
        browser.url('/core/list.html');
        browser.keys('\uE014'); // Right
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('goes to Full mode when a slide is clicked', () => {
        browser.url('/core/list.html');
        browser.click('[id="1"]');
        browser.isExisting('.shower.full').should.equal(true);
    });

    // Traverse

    it('changes slide states while moving forward', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.keys('\uE014'); // Right
        browser.isExisting('[id="1"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="ID"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="3"].active').should.equal(true);
    });

    it('changes slide states while moving backward', () => {
        browser.url('/core/list.html#3');
        browser.keys('\uE012'); // Left
        browser.keys('\uE012'); // Left
        browser.isExisting('[id="1"].active').should.equal(true);
        browser.isExisting('[id="ID"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="3"].visited:not(.active)').should.equal(true);
    });

    it('changes slide states while moving forward and backward', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.keys('\uE014'); // Right
        browser.keys('\uE012'); // Left
        browser.keys('\uE012'); // Left
        browser.isExisting('[id="1"].visited.active').should.equal(true);
        browser.isExisting('[id="ID"].visited:not(.active)').should.equal(true);
        browser.isExisting('[id="3"].visited:not(.active)').should.equal(true);
    });

    // History

    it('goes to previous and next slides while moving back and forward in history', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.keys('\uE014'); // Right
        browser.back();
        browser.back();
        browser.forward();
        browser.isExisting('[id="1"].visited').should.equal(true);
        browser.isExisting('[id="ID"].visited.active').should.equal(true);
        browser.isExisting('[id="3"].visited').should.equal(true);
    });

    // Forward

    it('moves forward when Right Arrow key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE014'); // Right
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Down Arrow key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE015'); // Down
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Page Down key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('\uE00F'); // Page Down
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when J key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('J');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when L key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('L');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Enter key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('Enter'); //
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('moves forward when Space key is pressed in Full mode', () => {
        browser.url('/core/list.html');
        browser.click('[id="1"]');
        browser.keys('Space');
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    it('doesn’t move forward when Space key is pressed in List mode', () => {
        browser.url('/core/list.html#1');
        browser.keys('Space');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    // Backward

    it('moves backward when Left Arrow key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('\uE012'); // Left
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Up Arrow key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('\uE013'); // Up
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Page Up key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('\uE00E'); // Page Up
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when K key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('K');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when H key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('H');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('moves backward when Shift Enter keys are pressed in List mode', () => {
        browser.url('/core/list.html#ID');
        browser.keys(['Shift', 'Enter']);
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    xit('moves backward when Shift Enter keys are pressed in Full mode', () => {
        browser.url('/core/list.html');
        browser.click('[id="ID"]');
        browser.keys(['Shift', 'Enter']);
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('doesn’t move backward when Shift Space keys are pressed in List mode', () => {
        browser.url('/core/list.html#ID')
        browser.keys(['Shift', 'Space']);
        browser.isExisting('[id="ID"].active').should.equal(true);
    });

    xit('moves backward when Shift Space keys are pressed in Full mode', () => {
        browser.url('/core/list.html');
        browser.click('[id="ID"]');
        browser.keys(['Shift', 'Space']);
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('goes to first slide when Home key is pressed', () => {
        browser.url('/core/list.html#3');
        browser.keys('Home');
        browser.isExisting('[id="1"].active').should.equal(true);
    });

    it('goes to last slide when End key is pressed', () => {
        browser.url('/core/list.html#1');
        browser.keys('End');
        browser.isExisting('[id="3"].active').should.equal(true);
    });

    // Start End

    it('starts presenation from the first slide when F5 key is pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys('F5');
        browser.isExisting('[id="1"].active').should.equal(true);
        browser.isExisting('.shower.full').should.equal(true);
    });

    it('starts presenation from the current slide when Shift F5 keys are pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys(['Shift', 'F5']);
        browser.isExisting('[id="ID"].active').should.equal(true);
        browser.isExisting('.shower.full').should.equal(true);
    });

    xit('starts presenation from the first slide when Cmd Shift Enter keys are pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys(['Meta', 'Shift', 'Enter']);
        browser.isExisting('[id="1"].active').should.equal(true);
        browser.isExisting('.shower.full').should.equal(true);
    });

    xit('starts presenation from the current slide when Cmd Enter keys are pressed', () => {
        browser.url('/core/list.html#ID');
        browser.keys(['Meta', 'Enter']);
        browser.isExisting('[id="ID"].active').should.equal(true);
        browser.isExisting('.shower.full').should.equal(true);
    });

    it('stops presenation when F5 key is pressed', () => {
        browser.url('/core/list.html');
        browser.click('[id="1"]');
        browser.keys('F5');
        browser.isExisting('.shower.list').should.equal(true);
    });

    it('stops presenation when Esc key is pressed', () => {
        browser.url('/core/list.html');
        browser.click('[id="1"]');
        browser.keys('Escape');
        browser.isExisting('.shower.list').should.equal(true);
    });

    // Title

    it('keeps page title unchanged in List mode', () => {
        browser.url('/core/title.html');
        browser.getTitle().should.equal('Title');
    });

    it('prepends page title with current slide title in Full mode', () => {
        browser.url('/core/title.html');
        browser.click('[id="1"]');
        browser.getTitle().should.equal('1 — Title')
    });

    it('doesn’t change page title in lack of slide title', () => {
        browser.url('/core/title.html');
        browser.click('[id="2"]');
        browser.getTitle().should.equal('Title');
    });

    it('strips tags from slide title while prepending', () => {
        browser.url('/core/title.html');
        browser.click('[id="3"]');
        browser.getTitle().should.equal('3 — Title');
    });

});
<|endoftext|>


<commit_before>// @flow

const Promise = require("bluebird");
const OpenShiftClient = require('openshift-client');
const sleep = require("es7-sleep");
const R = require('ramda');
const { logger } = require('@lagoon/commons/src/local-logging');
const {
  getOpenShiftInfoForProject,
  getEnvironmentByName,
  updateEnvironment,
  getDeploymentByRemoteId,
  getDeploymentByName,
  updateDeployment,
  setEnvironmentServices,
} = require('@lagoon/commons/src/api');

const { sendToLagoonLogs, initSendToLagoonLogs } = require('@lagoon/commons/src/logs');
const { consumeTaskMonitor, initSendToLagoonTasks } = require('@lagoon/commons/src/tasks');

class BuildNotCompletedYet extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'BuildNotCompletedYet';
  }
}

initSendToLagoonLogs();
initSendToLagoonTasks();

const messageConsumer = async msg => {
  const {
    buildName,
    projectName,
    openshiftProject,
    branchName,
    sha
  } = JSON.parse(msg.content.toString())

  logger.verbose(`Received BuildDeployOpenshift monitoring task for project: ${projectName}, buildName: ${buildName}, openshiftProject: ${openshiftProject}, branch: ${branchName}, sha: ${sha}`);
  const projectResult = await getOpenShiftInfoForProject(projectName);
  const project = projectResult.project

  const environmentResult = await getEnvironmentByName(branchName, project.id)
  const environment = environmentResult.environmentByName

  const deploymentResult = await getDeploymentByName(projectName, buildName);
  const deployment = deploymentResult.environment.deployments[0];

  try {
    var gitSha = sha
    var openshiftConsole = project.openshift.consoleUrl.replace(/\/$/, "");
    var openshiftToken = project.openshift.token || ""
  } catch(error) {
    logger.warn(`Error while loading information for project ${projectName}: ${error}`)
    throw(error)
  }

  // OpenShift API object
  const openshift = new OpenShiftClient.OApi({
    url: openshiftConsole,
    insecureSkipTlsVerify: true,
    auth: {
      bearer: openshiftToken
    },
  });

  // Kubernetes API Object - needed as some API calls are done to the Kubernetes API part of OpenShift and
  // the OpenShift API does not support them.
  const kubernetes = new OpenShiftClient.Core({
    url: openshiftConsole,
    insecureSkipTlsVerify: true,
    auth: {
      bearer: openshiftToken
    },
  });

  // kubernetes-client does not know about the OpenShift Resources, let's teach it.
  openshift.ns.addResource('builds');
  openshift.ns.addResource('deploymentconfigs');

  let projectStatus = {}
  try {
    const projectsGet = Promise.promisify(openshift.projects(openshiftProject).get, { context: openshift.projects(openshiftProject) })
    projectStatus = await projectsGet()
  } catch (err) {
    // a non existing project also throws an error, we check if it's a 404, means it does not exist, so we create it.
    if (err.code == 404) {
      logger.error(`Project ${openshiftProject} does not exist, bailing`)
      return
    } else {
      logger.error(err)
      throw new Error
    }
  }

  try {
    const buildsGet = Promise.promisify(openshift.ns(openshiftProject).builds(buildName).get, { context: openshift.ns(openshiftProject).builds(buildName) })
    buildstatus = await buildsGet()
  } catch (err) {
    if (err.code == 404) {
      logger.error(`Build ${buildName} does not exist, bailing`)
      return
    } else {
      logger.error(err)
      throw new Error
    }
  }

  const buildPhase = buildstatus.status.phase.toLowerCase();
  const buildsLogGet = Promise.promisify(openshift.ns(openshiftProject).builds(`${buildName}/log`).get, { context: openshift.ns(openshiftProject).builds(`${buildName}/log`) })
  const routesGet = Promise.promisify(openshift.ns(openshiftProject).routes.get, { context: openshift.ns(openshiftProject).routes })

  try {
    const deployment = await getDeploymentByRemoteId(buildstatus.metadata.uid);
    if (!deployment.deploymentByRemoteId) {
      throw new Error(`No deployment found with remote id ${buildstatus.metadata.uid}`);
    }

    const convertDateFormat = R.init;
    const dateOrNull = R.unless(R.isNil, convertDateFormat);

    await updateDeployment(deployment.deploymentByRemoteId.id, {
      status: buildstatus.status.phase.toUpperCase(),
      created: convertDateFormat(buildstatus.metadata.creationTimestamp),
      started: dateOrNull(buildstatus.status.startTimestamp),
      completed: dateOrNull(buildstatus.status.completionTimestamp),
    });
  } catch (error) {
    logger.error(`Could not update deployment ${projectName} ${buildName}. Message: ${error}`);
  }

  const meta = JSON.parse(msg.content.toString())
  const logLink = deployment.uiLink;
  let logMessage = ''
  if (sha) {
    meta.shortSha = sha.substring(0, 7)
    logMessage = `\`${branchName}\` (${sha.substring(0, 7)})`
  } else {
    logMessage = `\`${branchName}\``
  }
  switch (buildPhase) {
    case "new":
    case "pending":
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` not yet started`
      )
      throw new BuildNotCompletedYet(`*[${projectName}]* ${logMessage} Build \`${buildName}\` not yet started`)
      break;

    case "running":
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` running`
      )
      throw new BuildNotCompletedYet(`*[${projectName}]* ${logMessage} Build \`${buildName}\` running`)
      break;

    case "cancelled":
    case "error":
      sendToLagoonLogs('warn', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` cancelled. <${logLink}|Logs>`
      )
      break;

    case "failed":
      sendToLagoonLogs('error', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` failed. <${logLink}|Logs>`
      )
      break;

    case "complete":
      try {
        const buildLog = await buildsLogGet()
        const s3UploadResult = await saveBuildLog(buildName, projectName, branchName, buildLog, buildstatus)
        logLink = s3UploadResult.Location
        meta.logLink = logLink
      } catch (err) {
        logger.warn(`${openshiftProject} ${buildName}: Error while getting and uploading Logs to S3, Error: ${err}. Continuing without log link in message`)
        meta.logLink = ''
      }

      try {
        const configMapGet = Promise.promisify(kubernetes.ns(openshiftProject).configmaps('lagoon-env').get, { context: kubernetes.ns(openshiftProject).configmaps('lagoon-env') })
        configMap = await configMapGet()
      } catch (err) {
        if (err.code == 404) {
          logger.error(`configmap lagoon-env does not exist, continuing without routes information`)
        } else {
          logger.error(err)
          throw new Error
        }
      }

      const route = configMap.data.LAGOON_ROUTE
      const routes = configMap.data.LAGOON_ROUTES.split(',').filter(e => e !== route);
      meta.route = route
      meta.routes = routes
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` complete. <${logLink}|Logs> \n ${route}\n ${routes.join("\n")}`
      )
      try {
        const updateEnvironmentResult = await updateEnvironment(
          environment.id,
          `{
            route: "${configMap.data.LAGOON_ROUTE}",
            routes: "${configMap.data.LAGOON_ROUTES}",
            monitoringUrls: "${configMap.data.LAGOON_MONITORING_URLS}",
            project: ${project.id}
          }`)
        } catch (err) {
          logger.warn(`${openshiftProject} ${buildName}: Error while updating routes in API, Error: ${err}. Continuing without update`)
        }

      // Tell api what services are running in this environment
      try {
        // Get pod template from existing service
        const deploymentConfigsGet = Promise.promisify(
          openshift.ns(openshiftProject).deploymentconfigs.get, { context: openshift.ns(openshiftProject).deploymentconfigs }
        );
        const deploymentConfigs = await deploymentConfigsGet();

        const serviceNames = deploymentConfigs.items.reduce(
          (names, deploymentConfig) => [
            ...names,
            ...deploymentConfig.spec.template.spec.containers.reduce(
              (names, container) => [
                ...names,
                container.name
              ],
              []
            )
          ],
          []
        );

        await setEnvironmentServices(environment.id, serviceNames);
      } catch (err) {
        logger.error(`${openshiftProject} ${buildName}: Error while updating environment services in API, Error: ${err}`)
      }
      break;

    default:
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` phase ${buildPhase}`
      )
      throw new BuildNotCompletedYet(`*[${projectName}]* ${logMessage} Build \`${buildName}\` phase ${buildPhase}`)
      break;
  }

}

const deathHandler = async (msg, lastError) => {
  const {
    buildName,
    projectName,
    openshiftProject,
    branchName,
    sha
  } = JSON.parse(msg.content.toString())

  let logMessage = ''
  if (sha) {
    logMessage = `\`${branchName}\` (${sha.substring(0, 7)})`
  } else {
    logMessage = `\`${branchName}\``
  }

  sendToLagoonLogs('error', projectName, "", "task:builddeploy-openshift:error",  {},
`*[${projectName}]* ${logMessage} Build \`${buildName}\` ERROR:
\`\`\`
${lastError}
\`\`\``
  )

}

consumeTaskMonitor('builddeploy-openshift', messageConsumer, deathHandler)
<commit_msg>use openshiftproject instead of projectname<commit_after>// @flow

const Promise = require("bluebird");
const OpenShiftClient = require('openshift-client');
const sleep = require("es7-sleep");
const R = require('ramda');
const { logger } = require('@lagoon/commons/src/local-logging');
const {
  getOpenShiftInfoForProject,
  getEnvironmentByName,
  updateEnvironment,
  getDeploymentByRemoteId,
  getDeploymentByName,
  updateDeployment,
  setEnvironmentServices,
} = require('@lagoon/commons/src/api');

const { sendToLagoonLogs, initSendToLagoonLogs } = require('@lagoon/commons/src/logs');
const { consumeTaskMonitor, initSendToLagoonTasks } = require('@lagoon/commons/src/tasks');

class BuildNotCompletedYet extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'BuildNotCompletedYet';
  }
}

initSendToLagoonLogs();
initSendToLagoonTasks();

const messageConsumer = async msg => {
  const {
    buildName,
    projectName,
    openshiftProject,
    branchName,
    sha
  } = JSON.parse(msg.content.toString())

  logger.verbose(`Received BuildDeployOpenshift monitoring task for project: ${projectName}, buildName: ${buildName}, openshiftProject: ${openshiftProject}, branch: ${branchName}, sha: ${sha}`);
  const projectResult = await getOpenShiftInfoForProject(projectName);
  const project = projectResult.project

  const environmentResult = await getEnvironmentByName(branchName, project.id)
  const environment = environmentResult.environmentByName

  const deploymentResult = await getDeploymentByName(openshiftProject, buildName);
  const deployment = deploymentResult.environment.deployments[0];

  try {
    var gitSha = sha
    var openshiftConsole = project.openshift.consoleUrl.replace(/\/$/, "");
    var openshiftToken = project.openshift.token || ""
  } catch(error) {
    logger.warn(`Error while loading information for project ${projectName}: ${error}`)
    throw(error)
  }

  // OpenShift API object
  const openshift = new OpenShiftClient.OApi({
    url: openshiftConsole,
    insecureSkipTlsVerify: true,
    auth: {
      bearer: openshiftToken
    },
  });

  // Kubernetes API Object - needed as some API calls are done to the Kubernetes API part of OpenShift and
  // the OpenShift API does not support them.
  const kubernetes = new OpenShiftClient.Core({
    url: openshiftConsole,
    insecureSkipTlsVerify: true,
    auth: {
      bearer: openshiftToken
    },
  });

  // kubernetes-client does not know about the OpenShift Resources, let's teach it.
  openshift.ns.addResource('builds');
  openshift.ns.addResource('deploymentconfigs');

  let projectStatus = {}
  try {
    const projectsGet = Promise.promisify(openshift.projects(openshiftProject).get, { context: openshift.projects(openshiftProject) })
    projectStatus = await projectsGet()
  } catch (err) {
    // a non existing project also throws an error, we check if it's a 404, means it does not exist, so we create it.
    if (err.code == 404) {
      logger.error(`Project ${openshiftProject} does not exist, bailing`)
      return
    } else {
      logger.error(err)
      throw new Error
    }
  }

  try {
    const buildsGet = Promise.promisify(openshift.ns(openshiftProject).builds(buildName).get, { context: openshift.ns(openshiftProject).builds(buildName) })
    buildstatus = await buildsGet()
  } catch (err) {
    if (err.code == 404) {
      logger.error(`Build ${buildName} does not exist, bailing`)
      return
    } else {
      logger.error(err)
      throw new Error
    }
  }

  const buildPhase = buildstatus.status.phase.toLowerCase();
  const buildsLogGet = Promise.promisify(openshift.ns(openshiftProject).builds(`${buildName}/log`).get, { context: openshift.ns(openshiftProject).builds(`${buildName}/log`) })
  const routesGet = Promise.promisify(openshift.ns(openshiftProject).routes.get, { context: openshift.ns(openshiftProject).routes })

  try {
    const deployment = await getDeploymentByRemoteId(buildstatus.metadata.uid);
    if (!deployment.deploymentByRemoteId) {
      throw new Error(`No deployment found with remote id ${buildstatus.metadata.uid}`);
    }

    const convertDateFormat = R.init;
    const dateOrNull = R.unless(R.isNil, convertDateFormat);

    await updateDeployment(deployment.deploymentByRemoteId.id, {
      status: buildstatus.status.phase.toUpperCase(),
      created: convertDateFormat(buildstatus.metadata.creationTimestamp),
      started: dateOrNull(buildstatus.status.startTimestamp),
      completed: dateOrNull(buildstatus.status.completionTimestamp),
    });
  } catch (error) {
    logger.error(`Could not update deployment ${projectName} ${buildName}. Message: ${error}`);
  }

  const meta = JSON.parse(msg.content.toString())
  const logLink = deployment.uiLink;
  let logMessage = ''
  if (sha) {
    meta.shortSha = sha.substring(0, 7)
    logMessage = `\`${branchName}\` (${sha.substring(0, 7)})`
  } else {
    logMessage = `\`${branchName}\``
  }
  switch (buildPhase) {
    case "new":
    case "pending":
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` not yet started`
      )
      throw new BuildNotCompletedYet(`*[${projectName}]* ${logMessage} Build \`${buildName}\` not yet started`)
      break;

    case "running":
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` running`
      )
      throw new BuildNotCompletedYet(`*[${projectName}]* ${logMessage} Build \`${buildName}\` running`)
      break;

    case "cancelled":
    case "error":
      sendToLagoonLogs('warn', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` cancelled. <${logLink}|Logs>`
      )
      break;

    case "failed":
      sendToLagoonLogs('error', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` failed. <${logLink}|Logs>`
      )
      break;

    case "complete":
      try {
        const buildLog = await buildsLogGet()
        const s3UploadResult = await saveBuildLog(buildName, projectName, branchName, buildLog, buildstatus)
        logLink = s3UploadResult.Location
        meta.logLink = logLink
      } catch (err) {
        logger.warn(`${openshiftProject} ${buildName}: Error while getting and uploading Logs to S3, Error: ${err}. Continuing without log link in message`)
        meta.logLink = ''
      }

      try {
        const configMapGet = Promise.promisify(kubernetes.ns(openshiftProject).configmaps('lagoon-env').get, { context: kubernetes.ns(openshiftProject).configmaps('lagoon-env') })
        configMap = await configMapGet()
      } catch (err) {
        if (err.code == 404) {
          logger.error(`configmap lagoon-env does not exist, continuing without routes information`)
        } else {
          logger.error(err)
          throw new Error
        }
      }

      const route = configMap.data.LAGOON_ROUTE
      const routes = configMap.data.LAGOON_ROUTES.split(',').filter(e => e !== route);
      meta.route = route
      meta.routes = routes
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` complete. <${logLink}|Logs> \n ${route}\n ${routes.join("\n")}`
      )
      try {
        const updateEnvironmentResult = await updateEnvironment(
          environment.id,
          `{
            route: "${configMap.data.LAGOON_ROUTE}",
            routes: "${configMap.data.LAGOON_ROUTES}",
            monitoringUrls: "${configMap.data.LAGOON_MONITORING_URLS}",
            project: ${project.id}
          }`)
        } catch (err) {
          logger.warn(`${openshiftProject} ${buildName}: Error while updating routes in API, Error: ${err}. Continuing without update`)
        }

      // Tell api what services are running in this environment
      try {
        // Get pod template from existing service
        const deploymentConfigsGet = Promise.promisify(
          openshift.ns(openshiftProject).deploymentconfigs.get, { context: openshift.ns(openshiftProject).deploymentconfigs }
        );
        const deploymentConfigs = await deploymentConfigsGet();

        const serviceNames = deploymentConfigs.items.reduce(
          (names, deploymentConfig) => [
            ...names,
            ...deploymentConfig.spec.template.spec.containers.reduce(
              (names, container) => [
                ...names,
                container.name
              ],
              []
            )
          ],
          []
        );

        await setEnvironmentServices(environment.id, serviceNames);
      } catch (err) {
        logger.error(`${openshiftProject} ${buildName}: Error while updating environment services in API, Error: ${err}`)
      }
      break;

    default:
      sendToLagoonLogs('info', projectName, "", `task:builddeploy-openshift:${buildPhase}`, meta,
        `*[${projectName}]* ${logMessage} Build \`${buildName}\` phase ${buildPhase}`
      )
      throw new BuildNotCompletedYet(`*[${projectName}]* ${logMessage} Build \`${buildName}\` phase ${buildPhase}`)
      break;
  }

}

const deathHandler = async (msg, lastError) => {
  const {
    buildName,
    projectName,
    openshiftProject,
    branchName,
    sha
  } = JSON.parse(msg.content.toString())

  let logMessage = ''
  if (sha) {
    logMessage = `\`${branchName}\` (${sha.substring(0, 7)})`
  } else {
    logMessage = `\`${branchName}\``
  }

  sendToLagoonLogs('error', projectName, "", "task:builddeploy-openshift:error",  {},
`*[${projectName}]* ${logMessage} Build \`${buildName}\` ERROR:
\`\`\`
${lastError}
\`\`\``
  )

}

consumeTaskMonitor('builddeploy-openshift', messageConsumer, deathHandler)
<|endoftext|>


<commit_before>'use strict';

let cli      = require('heroku-cli-util');
let co       = require('co');
let _        = require('lodash');
let strftime = require('strftime');

let trunc = s => _.trunc(s, {length: 35, omission: '…'});

function printJSON (data) {
  cli.log(JSON.stringify(data.dynos, null, 2));
}

function timeAgo (since) {
  let elapsed = Math.floor((new Date() - since)/1000);
  let message = strftime('%Y/%m/%d %H:%M:%S', since);
  if (elapsed <= 60) return `${message} (~ ${Math.floor(elapsed)}s ago)`;
  else if (elapsed <= 60*60) return `${message} (~ ${Math.floor(elapsed/60)}m ago)`;
  else if (elapsed <= 60*60*25) return `${message} (~ ${Math.floor(elapsed/60/60)}h ago)`;
  else return message;
}

function timeRemaining (from, to) {
  let secs  = Math.floor(to/1000 - from/1000);
  let mins  = Math.floor(secs / 60);
  let hours = Math.floor(mins / 60);
  if (hours > 0) return `${hours}h ${mins % 60}m`;
  if (mins > 0)  return `${mins}m ${secs % 60}s`;
  if (secs > 0)  return `${secs}s`;
  return '';
}

function printQuota (quota) {
  let lbl;
  if (quota.allow_until) lbl = 'Free quota left';
  else if (quota.deny_until) lbl = 'Free quota exhausted. Unidle available in';
  if (lbl) {
    let timestamp = quota.allow_until ? new Date(quota.allow_until) : new Date(quota.deny_until);
    let time = timeRemaining(new Date(), timestamp);
    console.log(`${lbl}: ${time}`);
  }
}

function printExtended (dynos) {
  cli.table(dynos, {
    columns: [
      {key: 'id', label: 'ID'},
      {key: 'name', label: 'Process'},
      {key: 'state', label: 'State', format: (state, row) => `${state} ${timeAgo(new Date(row.updated_at))}`},
      {key: 'extended.region', label: 'Region'},
      {key: 'extended.instance', label: 'Instance'},
      {key: 'extended.port', label: 'Port'},
      {key: 'extended.az', label: 'AZ'},
      {key: 'release.version', label: 'Release'},
      {key: 'command', label: 'Command', format: trunc},
      {key: 'extended.route', label: 'Route'},
      {key: 'size', label: 'Size'},
    ]
  });
}

function printDynos (dynos) {
  let dynosByCommand = _.reduce(dynos, function (dynosByCommand, dyno) {
    let since = timeAgo(new Date(dyno.updated_at));
    let size = dyno.size || '1X';

    if (dyno.type === 'run') {
      let key = `run: one-off processes`;
      if (dynosByCommand[key] === undefined) dynosByCommand[key] = [];
      dynosByCommand[key].push(`${dyno.name} (${size}): ${dyno.state} ${since}: ${dyno.command}`);
    } else {
      let key = `${dyno.type} (${size}): ${dyno.command}`;
      if (dynosByCommand[key] === undefined) dynosByCommand[key] = [];
      let item = `${dyno.name}: ${dyno.state} ${since}`;
      dynosByCommand[key].push(item);
    }
    return dynosByCommand;
  }, {});
  for (let key of Object.keys(dynosByCommand).sort()) {
    cli.styledHeader(key);
    for (let dyno of dynosByCommand[key]) cli.log(dyno);
    cli.log();
  }
}

function* run (context, heroku) {
  let suffix = context.flags.extended ? '?extended=true' : '';
  let data = yield {
    quota: heroku.request({path: `/apps/${context.app}/actions/get-quota${suffix}`, method: 'post', headers: {accept: 'application/vnd.heroku+json; version=3.app-quotas'}}),
    dynos: heroku.request({path: `/apps/${context.app}/dynos${suffix}`}),
  };
  if (context.flags.json) {
    printJSON(data);
  } else if (context.flags.extended) {
    printExtended(data.dynos);
  } else {
    printQuota(data.quota);
    printDynos(data.dynos);
  }
}

module.exports = {
  topic: 'ps',
  description: 'list dynos for an app',
  flags: [
    {name: 'json', description: 'display as json'},
    {name: 'extended', char: 'x', hidden: true},
  ],
  help: `
Example:

 $ heroku ps
 === run: one-off dyno
 run.1: up for 5m: bash

 === web: bundle exec thin start -p $PORT
 web.1: created for 30s`,
  needsAuth: true,
  needsApp: true,
  run: cli.command(co.wrap(run))
};
<commit_msg>do not fail if quota is unavailable<commit_after>'use strict';

let cli      = require('heroku-cli-util');
let co       = require('co');
let _        = require('lodash');
let strftime = require('strftime');

let trunc = s => _.trunc(s, {length: 35, omission: '…'});

function printJSON (data) {
  cli.log(JSON.stringify(data.dynos, null, 2));
}

function timeAgo (since) {
  let elapsed = Math.floor((new Date() - since)/1000);
  let message = strftime('%Y/%m/%d %H:%M:%S', since);
  if (elapsed <= 60) return `${message} (~ ${Math.floor(elapsed)}s ago)`;
  else if (elapsed <= 60*60) return `${message} (~ ${Math.floor(elapsed/60)}m ago)`;
  else if (elapsed <= 60*60*25) return `${message} (~ ${Math.floor(elapsed/60/60)}h ago)`;
  else return message;
}

function timeRemaining (from, to) {
  let secs  = Math.floor(to/1000 - from/1000);
  let mins  = Math.floor(secs / 60);
  let hours = Math.floor(mins / 60);
  if (hours > 0) return `${hours}h ${mins % 60}m`;
  if (mins > 0)  return `${mins}m ${secs % 60}s`;
  if (secs > 0)  return `${secs}s`;
  return '';
}

function printQuota (quota) {
  if (!quota) return;
  let lbl;
  if (quota.allow_until) lbl = 'Free quota left';
  else if (quota.deny_until) lbl = 'Free quota exhausted. Unidle available in';
  if (lbl) {
    let timestamp = quota.allow_until ? new Date(quota.allow_until) : new Date(quota.deny_until);
    let time = timeRemaining(new Date(), timestamp);
    console.log(`${lbl}: ${time}`);
  }
}

function printExtended (dynos) {
  cli.table(dynos, {
    columns: [
      {key: 'id', label: 'ID'},
      {key: 'name', label: 'Process'},
      {key: 'state', label: 'State', format: (state, row) => `${state} ${timeAgo(new Date(row.updated_at))}`},
      {key: 'extended.region', label: 'Region'},
      {key: 'extended.instance', label: 'Instance'},
      {key: 'extended.port', label: 'Port'},
      {key: 'extended.az', label: 'AZ'},
      {key: 'release.version', label: 'Release'},
      {key: 'command', label: 'Command', format: trunc},
      {key: 'extended.route', label: 'Route'},
      {key: 'size', label: 'Size'},
    ]
  });
}

function printDynos (dynos) {
  let dynosByCommand = _.reduce(dynos, function (dynosByCommand, dyno) {
    let since = timeAgo(new Date(dyno.updated_at));
    let size = dyno.size || '1X';

    if (dyno.type === 'run') {
      let key = `run: one-off processes`;
      if (dynosByCommand[key] === undefined) dynosByCommand[key] = [];
      dynosByCommand[key].push(`${dyno.name} (${size}): ${dyno.state} ${since}: ${dyno.command}`);
    } else {
      let key = `${dyno.type} (${size}): ${dyno.command}`;
      if (dynosByCommand[key] === undefined) dynosByCommand[key] = [];
      let item = `${dyno.name}: ${dyno.state} ${since}`;
      dynosByCommand[key].push(item);
    }
    return dynosByCommand;
  }, {});
  for (let key of Object.keys(dynosByCommand).sort()) {
    cli.styledHeader(key);
    for (let dyno of dynosByCommand[key]) cli.log(dyno);
    cli.log();
  }
}

function* run (context, heroku) {
  let suffix = context.flags.extended ? '?extended=true' : '';
  let data = yield {
    quota: heroku.request({
      path: `/apps/${context.app}/actions/get-quota${suffix}`,
      method: 'post', headers: {accept: 'application/vnd.heroku+json; version=3.app-quotas'}
    }).catch(() => {}),
    dynos: heroku.request({path: `/apps/${context.app}/dynos${suffix}`}),
  };
  if (context.flags.json) {
    printJSON(data);
  } else if (context.flags.extended) {
    printExtended(data.dynos);
  } else {
    printQuota(data.quota);
    printDynos(data.dynos);
  }
}

module.exports = {
  topic: 'ps',
  description: 'list dynos for an app',
  flags: [
    {name: 'json', description: 'display as json'},
    {name: 'extended', char: 'x', hidden: true},
  ],
  help: `
Example:

 $ heroku ps
 === run: one-off dyno
 run.1: up for 5m: bash

 === web: bundle exec thin start -p $PORT
 web.1: created for 30s`,
  needsAuth: true,
  needsApp: true,
  run: cli.command(co.wrap(run))
};
<|endoftext|>