/** * @module serviceControl */ // @flow const _ = require('lodash'); const Promise = require('bluebird'); const universe = require('../universe'); const serviceControlNS = universe.namespaceFactory('_cukelib'); const { get, set, log3, unset, hasKey, getCukeContext, initializeWith } = serviceControlNS; const listServices = (serviceRoot: string, depth: number) => { let serviceList = []; _.forIn(get(serviceRoot ? `_services.${serviceRoot}` : '_services'), (candidate, name) => { const namePath = serviceRoot ? `${serviceRoot}.${name}` : name; if (candidate.cukeContext) { serviceList.push(namePath); } else if (depth < 10) { const deepServiceList = listServices(namePath, depth + 1); serviceList = serviceList.concat(deepServiceList); } }); return serviceList; }; const serviceControl = module.exports = { serviceControlNS, addBoilerPlate(prefix: string, serviceObject: Object) { return _.defaults(serviceObject, { initialize() { return serviceControl.initialize.call(this); }, getService(name) { return serviceControl.getService(`${prefix}.${name}`); }, }); }, initialize() { if (hasKey('_services')) return; initializeWith.call(this, { _services: {} }); const cukeContextKiller = (cukeContext) => { const killTheseServices = listServices('', 0) .filter((namePath) => get(`_services.${namePath}`).cukeContext === cukeContext); log3('log3', 'killTheseServices', killTheseServices); return Promise.map(killTheseServices, (name) => serviceControl.stopService(name)); }; this.After(cukeContextKiller.bind(null, 'scenario')); this.registerHandler('AfterFeature', cukeContextKiller.bind(null, 'feature')); this.registerHandler('AfterFeatures', cukeContextKiller.bind(null, 'universe')); }, stopService(name: string) { log3('log3', 'serviceControl/stopService', name); const service = get(`_services.${name}`); if (!service) return Promise.resolve(`no service for ${name}`); unset(`_services.${name}`); if (service.stop) { return service.stop(); } else if ((service.proc || {}).kill) { service.removeListeners(); service.proc.kill('SIGTERM'); return service.exitPromise; } throw new Error(`Don't know how to stop service "${name}"`); }, /** * @param {string} name service name * * @returns {Object} universe service object root */ getService(name: string) { return get(`_services.${name}`); }, launchService(name: string, start: () => Object) { log3('log3', 'serviceControl/launchService', name); if (!get('_services')) { throw new Error('tried to launchService before service_control was initialized'); } return serviceControl.stopService(name) .then(start) .then((service) => { if (!service.stop || !_.isFunction(service.stop)) { throw new Error(`service '${name}' is missing a stop function`); } set(`_services.${name}`, service); service.name = name; // eslint-disable-line no-param-reassign service.cukeContext = getCukeContext(); // eslint-disable-line no-param-reassign return service; }); }, };