diff --git a/package.json b/package.json index 88220c30e..2995967f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "iofogcontroller", - "version": "1.0.32", + "version": "1.0.33", "description": "ioFog Controller project for Eclipse IoFog @ iofog.org \\nCopyright (c) 2018 Edgeworx, Inc.", "main": "./src/main.js", "author": "Saeid Baghbidi", diff --git a/src/cli/iofog.js b/src/cli/iofog.js index 8749dbb83..5a7606587 100644 --- a/src/cli/iofog.js +++ b/src/cli/iofog.js @@ -314,7 +314,7 @@ async function _deleteFog(obj, user) { } async function _getFogList(obj, user) { - const emptyFilters = [] + const emptyFilters = []; const list = await FogService.getFogList(emptyFilters, user, true); logger.info(JSON.stringify(list, null, 2)); logger.info('Fog list has been gotten successfully.'); @@ -359,8 +359,11 @@ async function _getHalHardwareInfo(obj) { logger.info("Parameters" + JSON.stringify(uuidObj)); - const info = await FogService.getHalHardwareInfo(uuidObj, {}, true); - logger.info(JSON.stringify(info, null, 2)); + const data = await FogService.getHalHardwareInfo(uuidObj, {}, true); + if (data.info) { + data.info = JSON.parse(data.info); + } + logger.info(JSON.stringify(data, null, 2)); logger.info('Hardware info has been retrieved successfully.') } @@ -371,8 +374,11 @@ async function _getHalUsbInfo(obj) { logger.info("Parameters" + JSON.stringify(uuidObj)); - const info = await FogService.getHalHardwareInfo(uuidObj, {}, true); - logger.info(JSON.stringify(info, null, 2)); + const data = await FogService.getHalUsbInfo(uuidObj, {}, true); + if (data.info) { + data.info = JSON.parse(data.info); + } + logger.info(JSON.stringify(data, null, 2)); logger.info('Usb info has been retrieved successfully.') } diff --git a/src/cli/microservice.js b/src/cli/microservice.js index 687c0fe52..812e63474 100644 --- a/src/cli/microservice.js +++ b/src/cli/microservice.js @@ -421,7 +421,7 @@ const _createMicroservice = async function (obj, user) { logger.info(JSON.stringify(microservice)); - const result = await MicroserviceService.createMicroserviceOnFog(microservice, user, true); + const result = await MicroserviceService.createMicroservice(microservice, user, true); logger.info(JSON.stringify(result)); logger.info('Microservice has been created successfully.'); }; diff --git a/src/config/development.json b/src/config/development.json index 9217782be..5dffbac92 100644 --- a/src/config/development.json +++ b/src/config/development.json @@ -29,5 +29,4 @@ "Diagnostics": { "DiagnosticDir": "diagnostic" } -} - +} \ No newline at end of file diff --git a/src/controllers/microservices-controller.js b/src/controllers/microservices-controller.js index e5501be60..6426854b3 100644 --- a/src/controllers/microservices-controller.js +++ b/src/controllers/microservices-controller.js @@ -20,7 +20,7 @@ const createMicroserviceOnFogEndPoint = async function (req, user) { logger.info("Parameters:" + JSON.stringify(microservice)); - return await MicroservicesService.createMicroserviceOnFog(microservice, user, false) + return await MicroservicesService.createMicroservice(microservice, user, false) }; const getMicroserviceEndPoint = async function (req, user) { diff --git a/src/helpers/app-helper.js b/src/helpers/app-helper.js index f6730b452..6dacf9799 100644 --- a/src/helpers/app-helper.js +++ b/src/helpers/app-helper.js @@ -78,13 +78,7 @@ const findAvailablePort = async function (hostname) { } let portBounds = portRange.split("-").map(i => parseInt(i)); return await portscanner.findAPortNotInUse(portBounds[0], portBounds[1], hostname); -} - -/** - * @desc generates a random String of the size specified by the input param - * @param Integer - size - * @return String - returns random string - */ +}; function isFileExists(filePath) { if (path.extname(filePath).indexOf(".") >= 0) { @@ -109,7 +103,7 @@ function isValidDomain(domain) { } const isValidPublicIP = function (publicIP) { - let re = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; + const re = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; return re.test(publicIP); }; @@ -142,7 +136,7 @@ function deleteUndefinedFields(obj) { } else if (obj[fld] instanceof Object) { obj[fld] = deleteUndefinedFields(obj[fld]) } - }) + }); return obj } @@ -191,11 +185,11 @@ function trimCertificate(cert) { function validateParameters(command, commandDefinitions, args) { // 1st argument = command args.shift(); - + const possibleAliasesList = _getPossibleAliasesList(command, commandDefinitions); const possibleArgsList = _getPossibleArgsList(command, commandDefinitions); - for (let arg of args) { + for (const arg of args) { // arg is [argument, alias, value] if (arg.startsWith("--")) { // argument @@ -272,14 +266,13 @@ function isTest() { } function isEmpty(obj) { - for(let key in obj) { - if(obj.hasOwnProperty(key)) + for (const key in obj) { + if (obj.hasOwnProperty(key)) return false; } return true; } - module.exports = { encryptText, decryptText, diff --git a/src/helpers/error-messages.js b/src/helpers/error-messages.js index 0ad086a46..37e68e8d7 100644 --- a/src/helpers/error-messages.js +++ b/src/helpers/error-messages.js @@ -73,5 +73,6 @@ module.exports = { INVALID_ROUTE: 'Route parsing error. Please provide valid route.' }, CONNECTOR_IS_IN_USE: 'You can\'t delete connector, because it is used for routing now.', - INVALID_VERSION_COMMAND: 'Can\'t {} version now.' + INVALID_VERSION_COMMAND_UPGRADE: 'Can\'t upgrade version now. Latest is already installed', + INVALID_VERSION_COMMAND_ROLLBACK: 'Can\'t rollback version now. There are no backups on agent' }; diff --git a/src/sequelize/managers/microservice-public-mode-manager.js b/src/sequelize/managers/microservice-public-mode-manager.js index 5d0561b69..e25e26968 100644 --- a/src/sequelize/managers/microservice-public-mode-manager.js +++ b/src/sequelize/managers/microservice-public-mode-manager.js @@ -13,12 +13,28 @@ const BaseManager = require('../managers/base-manager') const models = require('./../models'); -const MicroservicePublicMode = models.MicroservicePublicMode +const MicroservicePublicMode = models.MicroservicePublicMode; +const ConnectorPort = models.ConnectorPort; class MicroservicePublicModeManager extends BaseManager { getEntity() { return MicroservicePublicMode } + + findAllMicroservicePublicModesByConnectorId(connectorId, transaction) { + return MicroservicePublicMode.findAll({ + include: [ + { + model: ConnectorPort, + as: 'connectorPort', + required: true + } + ], + where: { + '$connectorPort.connector_id$': connectorId + } + }, {transaction: transaction}) + } } const instance = new MicroservicePublicModeManager() diff --git a/src/sequelize/managers/routing-manager.js b/src/sequelize/managers/routing-manager.js index 0ba42fd1e..df135f232 100644 --- a/src/sequelize/managers/routing-manager.js +++ b/src/sequelize/managers/routing-manager.js @@ -14,11 +14,27 @@ const BaseManager = require('./base-manager'); const models = require('./../models'); const Routing = models.Routing; +const ConnectorPort = models.ConnectorPort; class RoutingManager extends BaseManager { getEntity() { return Routing; } + + findAllRoutesByConnectorId(connectorId, transaction) { + return Routing.findAll({ + include: [ + { + model: ConnectorPort, + as: 'connectorPort', + required: true + } + ], + where: { + '$connectorPort.connector_id$': connectorId + } + }, {transaction: transaction}) + } } const instance = new RoutingManager(); diff --git a/src/services/connector-port-service.js b/src/services/connector-port-service.js new file mode 100644 index 000000000..7d8bb89ee --- /dev/null +++ b/src/services/connector-port-service.js @@ -0,0 +1,153 @@ +/* + * ******************************************************************************* + * * Copyright (c) 2018 Edgeworx, Inc. + * * + * * This program and the accompanying materials are made available under the + * * terms of the Eclipse Public License v. 2.0 which is available at + * * http://www.eclipse.org/legal/epl-2.0 + * * + * * SPDX-License-Identifier: EPL-2.0 + * ******************************************************************************* + * + */ + +const ConnectorManager = require('../sequelize/managers/connector-manager'); +const https = require('https'); +const http = require('http'); +const constants = require('../helpers/constants'); +const logger = require('../logger'); +const qs = require('qs'); +const fs = require('fs'); + +async function openPortOnRandomConnector(isPublicAccess, transaction) { + let isConnectorPortOpen = false; + let ports = null; + let connector = null; + const maxAttempts = 5; + for (let i = 0; i < maxAttempts; i++) { + try { + connector = await _getRandomConnector(transaction); + ports = await _openPortsOnConnector(connector, isPublicAccess); + if (ports) { + isConnectorPortOpen = true; + break; + } + } catch (e) { + logger.warn(`Failed to open ports on Connector. Attempts ${i + 1}/${maxAttempts}`) + } + } + if (!isConnectorPortOpen) { + throw new Error('Not able to open port on remote Connector. Gave up after 5 attempts.') + } + ports.connectorId = connector.id; + return {ports: ports, connector: connector} +} + +async function _openPortsOnConnector(connector, isPublicAccess) { + let data = isPublicAccess + ? await qs.stringify({ + mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' + }) + : await qs.stringify({ + mapping: '{"type":"private","maxconnectionsport1":1, "maxconnectionsport2":1, ' + + '"heartbeatabsencethresholdport1":200000, "heartbeatabsencethresholdport2":200000}' + }); + + let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; + + let options = { + host: connector.domain, + port: port, + path: '/api/v2/mapping/add', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(data) + } + }; + if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { + const ca = fs.readFileSync(connector.cert); + options.ca = new Buffer.from(ca); + } + + const ports = await _makeRequest(connector, options, data); + return ports +} + +async function _getRandomConnector(transaction) { + const connectors = await ConnectorManager.findAll({}, transaction); + + if (connectors && connectors.length > 0) { + const randomNumber = Math.round((Math.random() * (connectors.length - 1))); + return connectors[randomNumber] + } else { + throw new Error('no connectors defined') + } +} + +async function closePortOnConnector(connector, ports) { + let data = qs.stringify({ + mappingid: ports.mappingId + }); + console.log(data); + + let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; + + let options = { + host: connector.domain, + port: port, + path: '/api/v2/mapping/remove', + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': Buffer.byteLength(data) + } + }; + if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { + const ca = fs.readFileSync(connector.cert); + options.ca = new Buffer.from(ca); + } + + + await _makeRequest(connector, options, data) +} + +async function _makeRequest(connector, options, data) { + return new Promise((resolve, reject) => { + let httpreq = (connector.devMode ? http : https).request(options, function (response) { + console.log(response.statusCode); + let output = ''; + response.setEncoding('utf8'); + + response.on('data', function (chunk) { + output += chunk; + }); + + response.on('end', function () { + let responseObj = JSON.parse(output); + console.log(responseObj); + if (responseObj.errormessage) { + return reject(new Error(responseObj.errormessage)); + } else { + return resolve(responseObj); + } + }); + }); + + httpreq.on('error', function (err) { + console.log(err); + if (err instanceof Error) + return reject(new Error(err.message)); + else + return reject(new Error(JSON.stringify(err))); + }); + + httpreq.write(data); + httpreq.end(); + }) +} + +module.exports = { + openPortOnRandomConnector: openPortOnRandomConnector, + closePortOnConnector: closePortOnConnector +}; diff --git a/src/services/connector-service.js b/src/services/connector-service.js index 6b8809e8e..c902fc3f8 100644 --- a/src/services/connector-service.js +++ b/src/services/connector-service.js @@ -17,23 +17,25 @@ const ConnectorManager = require('../sequelize/managers/connector-manager'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); const AppHelper = require('../helpers/app-helper'); -const https = require('https'); -const http = require('http'); -const constants = require('../helpers/constants'); -const logger = require('../logger'); -const qs = require('qs'); const Op = require('sequelize').Op; -const fs = require('fs'); + const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); +const MicroserviceService = require('../services/microservices-service'); -async function _createConnector(connectorData, transaction) { +async function createConnector(connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorCreate); - validateConnectorData(connectorData); + _validateConnectorData(connectorData); const connector = await ConnectorManager.findOne({ [Op.or]: [ - {name: connectorData.name}, - {publicIp: connectorData.publicIp}, - {domain: connectorData.domain} + { + name: connectorData.name + }, + { + publicIp: connectorData.publicIp + }, + { + domain: connectorData.domain + } ] }, transaction); if (connector) { @@ -42,29 +44,19 @@ async function _createConnector(connectorData, transaction) { return await ConnectorManager.create(connectorData, transaction) } -async function _updateConnector(connectorData, transaction) { - await Validator.validate(connectorData, Validator.schemas.connectorUpdate) - validateConnectorData(connectorData); +async function updateConnector(connectorData, transaction) { + await Validator.validate(connectorData, Validator.schemas.connectorUpdate); + _validateConnectorData(connectorData); const queryConnectorData = { publicIp: connectorData.publicIp }; - await ConnectorManager.update(queryConnectorData, connectorData, transaction) + await ConnectorManager.update(queryConnectorData, connectorData, transaction); + const connector = await ConnectorManager.findOne({publicIp: connectorData.publicIp}, transaction); + await MicroserviceService.updateRouteOverConnector(connector, transaction); + await MicroserviceService.updatePortMappingOverConnector(connector, transaction); } -function validateConnectorData(connectorData) { - if (connectorData.domain) { - const validDomain = AppHelper.isValidDomain(connectorData.domain) || AppHelper.isValidPublicIP(connectorData.domain); - if (!validDomain) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_DOMAIN, connectorData.domain)); - } - } - - if (!AppHelper.isValidPublicIP(connectorData.publicIp)) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)); - } -} - -async function _deleteConnector(connectorData, transaction) { +async function deleteConnector(connectorData, transaction) { await Validator.validate(connectorData, Validator.schemas.connectorDelete); const queryConnectorData = { publicIp: connectorData.publicIp @@ -80,142 +72,26 @@ async function _deleteConnector(connectorData, transaction) { await ConnectorManager.delete(queryConnectorData, transaction); } -async function _getConnectorList(transaction) { +async function getConnectorList(transaction) { return await ConnectorManager.findAll({}, transaction) } -async function openPortOnRandomConnector(isPublicAccess, transaction) { - let isConnectorPortOpen = false; - let ports = null; - let connector = null; - const maxAttempts = 5; - for (let i = 0; i < maxAttempts; i++) { - try { - connector = await _getRandomConnector(transaction); - ports = await openPortsOnConnector(connector, isPublicAccess, transaction); - if (ports) { - isConnectorPortOpen = true; - break; - } - } catch (e) { - logger.warn(`Failed to open ports on comsat. Attempts ${i + 1}/${maxAttempts}`) - } - } - if (!isConnectorPortOpen) { - throw new Error('Not able to open port on remote CONNECTOR. Gave up after 5 attempts.') - } - ports.connectorId = connector.id; - return {ports: ports, connector: connector} -} - -async function _makeRequest(connector, options, data) { - return new Promise((resolve, reject) => { - let httpreq = (connector.devMode ? http : https).request(options, function (response) { - console.log(response.statusCode); - let output = ''; - response.setEncoding('utf8'); - - response.on('data', function (chunk) { - output += chunk; - }); - - response.on('end', function () { - let responseObj = JSON.parse(output); - console.log(responseObj); - if (responseObj.errormessage) { - return reject(new Error(responseObj.errormessage)); - } else { - return resolve(responseObj); - } - }); - }); - - httpreq.on('error', function (err) { - console.log(err); - if (err instanceof Error) - return reject(new Error(err.message)); - else - return reject(new Error(JSON.stringify(err))); - }); - - httpreq.write(data); - httpreq.end(); - }) -} - -async function openPortsOnConnector(connector, isPublicAccess, transaction) { - let data = isPublicAccess - ? await qs.stringify({ - mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' - }) - : await qs.stringify({ - mapping: '{"type":"private","maxconnectionsport1":1, "maxconnectionsport2":1, ' + - '"heartbeatabsencethresholdport1":200000, "heartbeatabsencethresholdport2":200000}' - }); - - let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; - - let options = { - host: connector.domain, - port: port, - path: '/api/v2/mapping/add', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(data) +function _validateConnectorData(connectorData) { + if (connectorData.domain) { + const validDomain = AppHelper.isValidDomain(connectorData.domain) || AppHelper.isValidPublicIP(connectorData.domain); + if (!validDomain) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_DOMAIN, connectorData.domain)); } - }; - if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { - const ca = fs.readFileSync(connector.cert); - options.ca = new Buffer.from(ca); } - const ports = await _makeRequest(connector, options, data); - return ports -} - -async function _getRandomConnector(transaction) { - const connectors = await _getConnectorList(transaction); - if (connectors && connectors.length > 0) { - const randomNumber = Math.round((Math.random() * (connectors.length - 1))); - return connectors[randomNumber] - } else { - throw new Error('no connectors defined') - } -} - -async function closePortOnConnector(connector, ports, transaction) { - let data = qs.stringify({ - mappingid: ports.mappingId - }); - console.log(data); - - let port = connector.devMode ? constants.CONNECTOR_HTTP_PORT : constants.CONNECTOR_HTTPS_PORT; - - let options = { - host: connector.domain, - port: port, - path: '/api/v2/mapping/remove', - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - 'Content-Length': Buffer.byteLength(data) - } - }; - if (!connector.devMode && connector.cert && connector.isSelfSignedCert === true) { - const ca = fs.readFileSync(connector.cert); - options.ca = new Buffer.from(ca); + if (!AppHelper.isValidPublicIP(connectorData.publicIp)) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_CONNECTOR_IP, connectorData.publicIp)); } - - - await _makeRequest(connector, options, data) } module.exports = { - createConnector: TransactionDecorator.generateTransaction(_createConnector), - updateConnector: TransactionDecorator.generateTransaction(_updateConnector), - deleteConnector: TransactionDecorator.generateTransaction(_deleteConnector), - getConnectorList: TransactionDecorator.generateTransaction(_getConnectorList), - openPortOnRandomConnector: openPortOnRandomConnector, - closePortOnConnector: closePortOnConnector -} \ No newline at end of file + createConnector: TransactionDecorator.generateTransaction(createConnector), + updateConnector: TransactionDecorator.generateTransaction(updateConnector), + deleteConnector: TransactionDecorator.generateTransaction(deleteConnector), + getConnectorList: TransactionDecorator.generateTransaction(getConnectorList) +}; \ No newline at end of file diff --git a/src/services/controller-service.js b/src/services/controller-service.js index 906c1cc35..d42aea13d 100644 --- a/src/services/controller-service.js +++ b/src/services/controller-service.js @@ -11,26 +11,26 @@ * */ -const FogTypesManager = require('../sequelize/managers/iofog-type-manager'); +const ioFogTypesManager = require('../sequelize/managers/iofog-type-manager'); const Config = require('../config'); const TransactionDecorator = require('../decorators/transaction-decorator'); const packageJson = require('../../package'); const getFogTypes = async function (isCLI, transaction) { - const fogTypes = await FogTypesManager.findAll({}, transaction); - let res = []; + const ioFogTypes = await ioFogTypesManager.findAll({}, transaction); + let response = []; - for (fogType of fogTypes) { - res.push({ - id: fogType.id, - name: fogType.name, - image: fogType.image, - description: fogType.description + for (ioFogType of ioFogTypes) { + response.push({ + id: ioFogType.id, + name: ioFogType.name, + image: ioFogType.image, + description: ioFogType.description }) } return { - fogTypes: res + fogTypes: response } }; @@ -59,7 +59,7 @@ const statusController = async function (isCLI) { }; const getVersion = async function (isCLI) { - return `Iofog-Controller version: ${packageJson.version}`; + return `ioFog-Controller version: ${packageJson.version}`; }; module.exports = { diff --git a/src/services/diagnostic-service.js b/src/services/diagnostic-service.js index 9e48de668..2f3349eeb 100644 --- a/src/services/diagnostic-service.js +++ b/src/services/diagnostic-service.js @@ -20,11 +20,10 @@ const MicroserviceService = require('../services/microservices-service'); const StraceDiagnosticManager = require('../sequelize/managers/strace-diagnostics-manager'); const ChangeTrackingService = require('./change-tracking-service'); const MicroserviceManager = require('../sequelize/managers/microservice-manager'); -const config = require('../config'); +const Config = require('../config'); const fs = require('fs'); const logger = require('../logger'); const ftpClient = require('ftp'); -const path = require('path'); const mime = require('mime'); @@ -60,7 +59,7 @@ const getMicroserviceStraceData = async function (uuid, data, user, isCLI, trans throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } - const dir = config.get('Diagnostics:DiagnosticDir') || 'diagnostics'; + const dir = Config.get('Diagnostics:DiagnosticDir') || 'diagnostics'; const filePath = dir + '/' + uuid; let result = straceData.buffer; @@ -93,7 +92,7 @@ const postMicroserviceStraceDatatoFtp = async function (uuid, data, user, isCLI, throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_STRACE, uuid)) } - const dir = config.get('Diagnostics:DiagnosticDir'); + const dir = Config.get('Diagnostics:DiagnosticDir'); const filePath = dir + '/' + uuid; _createDirectoryIfNotExists(dir); diff --git a/src/services/email-activation-code-service.js b/src/services/email-activation-code-service.js index 1e63963d8..a79709c91 100644 --- a/src/services/email-activation-code-service.js +++ b/src/services/email-activation-code-service.js @@ -14,7 +14,6 @@ const EmailActivationCodeManager = require('../sequelize/managers/email-activation-code-manager'); const AppHelper = require('../helpers/app-helper'); const ErrorMessages = require('../helpers/error-messages'); -const TransactionDecorator = require('../decorators/transaction-decorator'); const generateActivationCode = async function (transaction) { while (true) { @@ -52,20 +51,12 @@ const verifyActivationCode = async function (activationCode, transaction) { const deleteActivationCode = async function (activationCode, transaction) { return await EmailActivationCodeManager.delete({ activationCode: activationCode - }, transaction) + }, transaction); }; -const findActivationCodeByUserId = async function (userId, transaction) { - return await EmailActivationCodeManager.findOne({ - userId: userId - }, transaction) -}; - - module.exports = { generateActivationCode: generateActivationCode, saveActivationCode: saveActivationCode, verifyActivationCode: verifyActivationCode, - deleteActivationCode: deleteActivationCode, - findActivationCodeByUserId: TransactionDecorator.generateTransaction(findActivationCodeByUserId) + deleteActivationCode: deleteActivationCode }; \ No newline at end of file diff --git a/src/services/flow-service.js b/src/services/flow-service.js index 55c446850..3ef591730 100644 --- a/src/services/flow-service.js +++ b/src/services/flow-service.js @@ -16,13 +16,15 @@ const FlowManager = require('../sequelize/managers/flow-manager'); const AppHelper = require('../helpers/app-helper'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); -const Validation = require('../schemas'); +const Validator = require('../schemas'); const ChangeTrackingService = require('./change-tracking-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; -const _createFlow = async function (flowData, user, isCLI, transaction) { - await Validation.validate(flowData, Validation.schemas.flowCreate); +const createFlow = async function (flowData, user, isCLI, transaction) { + await Validator.validate(flowData, Validator.schemas.flowCreate); - await _checkForDuplicateName(flowData.name, {}, user.id, transaction); + await _checkForDuplicateName(flowData.name, null, user.id, transaction); const flowToCreate = { name: flowData.name, @@ -40,7 +42,7 @@ const _createFlow = async function (flowData, user, isCLI, transaction) { } }; -const _deleteFlow = async function (flowId, user, isCLI, transaction) { +const deleteFlow = async function (flowId, user, isCLI, transaction) { const whereObj = { id: flowId, userId: user.id @@ -52,25 +54,10 @@ const _deleteFlow = async function (flowId, user, isCLI, transaction) { await FlowManager.delete(where, transaction); }; -async function _updateChangeTrackingsByFlowId(flowId, transaction) { - const flowWithMicroservices = await FlowManager.findFlowMicroservices({id: flowId}, transaction); - if (!flowWithMicroservices) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)); - } - const onlyUnique = (value, index, self) => self.indexOf(value) === index; - const iofogUuids = flowWithMicroservices.microservices - .map(obj => obj.iofogUuid) - .filter(onlyUnique) - .filter(val => val !== null); - for (let iofogUuid of iofogUuids) { - await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceFull, transaction); - } -} - -const _updateFlow = async function (flowData, flowId, user, isCLI, transaction) { - await Validation.validate(flowData, Validation.schemas.flowUpdate); +const updateFlow = async function (flowData, flowId, user, isCLI, transaction) { + await Validator.validate(flowData, Validator.schemas.flowUpdate); - const oldFlow = await _getFlow(flowId, user, isCLI, transaction); + const oldFlow = await getFlow(flowId, user, isCLI, transaction); if (!oldFlow) { throw new Errors.NotFoundError(ErrorMessages.INVALID_FLOW_ID) } @@ -97,20 +84,7 @@ const _updateFlow = async function (flowData, flowId, user, isCLI, transaction) } }; -const _getFlow = async function (flowId, user, isCLI, transaction) { - const where = isCLI - ? {id: flowId} - : {id: flowId, userId: user.id}; - - const flow = await FlowManager.findOneExcludeFields(where, transaction); - - if (!flow) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)) - } - return flow -}; - -const _getUserFlows = async function (user, isCLI, transaction) { +const getUserFlows = async function (user, isCLI, transaction) { const flow = { userId: user.id }; @@ -121,17 +95,31 @@ const _getUserFlows = async function (user, isCLI, transaction) { } }; -const _getAllFlows = async function (isCLI, transaction) { +const getAllFlows = async function (isCLI, transaction) { const flows = await FlowManager.findAll({}, transaction); return { flows: flows } }; -const _checkForDuplicateName = async function (name, item, userId, transaction) { +const getFlow = async function (flowId, user, isCLI, transaction) { + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + + const flow = await FlowManager.findOneExcludeFields(where, transaction); + + if (!flow) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)) + } + return flow +}; + + +const _checkForDuplicateName = async function (name, flowId, userId, transaction) { if (name) { - const where = item.id - ? {name: name, id: {[Op.ne]: item.id, userId: userId}} + const where = flowId + ? {name: name, id: {[Op.ne]: flowId, userId: userId}} : {name: name, userId: userId}; const result = await FlowManager.findOne(where, transaction); @@ -141,12 +129,27 @@ const _checkForDuplicateName = async function (name, item, userId, transaction) } }; +async function _updateChangeTrackingsByFlowId(flowId, transaction) { + const flowWithMicroservices = await FlowManager.findFlowMicroservices({id: flowId}, transaction); + if (!flowWithMicroservices) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FLOW_ID, flowId)); + } + const onlyUnique = (value, index, self) => self.indexOf(value) === index; + const iofogUuids = flowWithMicroservices.microservices + .map(obj => obj.iofogUuid) + .filter(onlyUnique) + .filter(val => val !== null); + for (const iofogUuid of iofogUuids) { + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceFull, transaction); + } +} + module.exports = { - createFlow: TransactionDecorator.generateTransaction(_createFlow), - deleteFlow: TransactionDecorator.generateTransaction(_deleteFlow), - updateFlow: TransactionDecorator.generateTransaction(_updateFlow), - getFlowWithTransaction: TransactionDecorator.generateTransaction(_getFlow), - getUserFlows: TransactionDecorator.generateTransaction(_getUserFlows), - getAllFlows: TransactionDecorator.generateTransaction(_getAllFlows), - getFlow: _getFlow + createFlow: TransactionDecorator.generateTransaction(createFlow), + deleteFlow: TransactionDecorator.generateTransaction(deleteFlow), + updateFlow: TransactionDecorator.generateTransaction(updateFlow), + getUserFlows: TransactionDecorator.generateTransaction(getUserFlows), + getAllFlows: TransactionDecorator.generateTransaction(getAllFlows), + getFlowWithTransaction: TransactionDecorator.generateTransaction(getFlow), + getFlow: getFlow }; diff --git a/src/services/iofog-service.js b/src/services/iofog-service.js index 0776cee92..ef5002a4e 100644 --- a/src/services/iofog-service.js +++ b/src/services/iofog-service.js @@ -25,7 +25,7 @@ const USBInfoManager = require('../sequelize/managers/usb-info-manager'); const CatalogService = require('../services/catalog-service'); const MicroserviceManager = require('../sequelize/managers/microservice-manager'); -async function _createFog(fogData, user, isCli, transaction) { +async function createFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogCreate); let createFogData = { @@ -71,15 +71,15 @@ async function _createFog(fogData, user, isCli, transaction) { await _createBluetoothMicroserviceForFog(fog, null, user, transaction); } - await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction) + await ChangeTrackingService.update(createFogData.uuid, ChangeTrackingService.events.microserviceCommon, transaction); return res } -async function _updateFog(fogData, user, isCli, transaction) { +async function updateFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogUpdate); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -118,21 +118,21 @@ async function _updateFog(fogData, user, isCli, transaction) { let msChanged = false; - if (oldFog.bluetoothEnabled === true && fogData.bluetoothEnabled === false) { - await _deleteBluetoothMicroserviceByFog(fogData, transaction); + if (oldFog.abstractedHardwareEnabled === true && fogData.abstractedHardwareEnabled === false) { + await _deleteHalMicroserviceByFog(fogData, transaction); msChanged = true; } - if (oldFog.bluetoothEnabled === false && fogData.bluetoothEnabled === true) { - await _createBluetoothMicroserviceForFog(fogData, oldFog, user, transaction); + if (oldFog.abstractedHardwareEnabled === false && fogData.abstractedHardwareEnabled === true) { + await _createHalMicroserviceForFog(fogData, oldFog, user, transaction); msChanged = true; } - if (oldFog.abstractedHardwareEnabled === true && fogData.abstractedHardwareEnabled === false) { - await _deleteHalMicroseviceByFog(fogData, transaction); + if (oldFog.bluetoothEnabled === true && fogData.bluetoothEnabled === false) { + await _deleteBluetoothMicroserviceByFog(fogData, transaction); msChanged = true; } - if (oldFog.abstractedHardwareEnabled === false && fogData.abstractedHardwareEnabled === true) { - await _createHalMicroserviceForFog(fogData, oldFog, user, transaction); + if (oldFog.bluetoothEnabled === false && fogData.bluetoothEnabled === true) { + await _createBluetoothMicroserviceForFog(fogData, oldFog, user, transaction); msChanged = true; } @@ -141,10 +141,10 @@ async function _updateFog(fogData, user, isCli, transaction) { } } -async function _deleteFog(fogData, user, isCli, transaction) { +async function deleteFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogDelete); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -156,10 +156,10 @@ async function _deleteFog(fogData, user, isCli, transaction) { await _processDeleteCommand(fog, transaction) } -async function _getFog(fogData, user, isCli, transaction) { +async function getFog(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogGet); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -167,81 +167,84 @@ async function _getFog(fogData, user, isCli, transaction) { if (!fog) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FOG_NODE_UUID, fogData.uuid)) } - await _updateFogsConnectionStatus(fog, transaction); + + const updatedFog = await _updateFogsConnectionStatus(fog, transaction); return { - uuid: fog.uuid, - name: fog.name, - location: fog.location, - gpsMode: fog.gpsMode, - latitude: fog.latitude, - longitude: fog.longitude, - description: fog.description, - lastActive: fog.lastActive, - daemonStatus: fog.daemonStatus, - daemonOperatingDuration: fog.daemonOperatingDuration, - daemonLastStart: fog.daemonLastStart, - memoryUsage: fog.memoryUsage, - diskUsage: fog.diskUsage, - cpuUsage: fog.cpuUsage, - memoryViolation: fog.memoryViolation, - diskViolation: fog.diskViolation, - cpuViolation: fog.cpuViolation, - catalogItemStatus: fog.catalogItemStatus, - repositoryCount: fog.repositoryCount, - repositoryStatus: fog.repositoryStatus, - systemTime: fog.systemTime, - lastStatusTime: fog.lastStatusTime, - ipAddress: fog.ipAddress, - processedMessages: fog.processedMessages, - catalogItemMessageCounts: fog.catalogItemMessageCounts, - messageSpeed: fog.messageSpeed, - lastCommandTime: fog.lastCommandTime, - networkInterface: fog.networkInterface, - dockerUrl: fog.dockerUrl, - diskLimit: fog.diskLimit, - diskDirectory: fog.diskDirectory, - memoryLimit: fog.memoryLimit, - cpuLimit: fog.cpuLimit, - logLimit: fog.logLimit, - logDirectory: fog.logDirectory, - bluetoothEnabled: fog.bluetoothEnabled, - abstractedHardwareEnabled: fog.abstractedHardwareEnabled, - logFileCount: fog.logFileCount, - version: fog.version, - isReadyToUpgrade: fog.isReadyToUpgrade, - isReadyToRollback: fog.isReadyToRollback, - statusFrequency: fog.statusFrequency, - changeFrequency: fog.changeFrequency, - deviceScanFrequency: fog.deviceScanFrequency, - tunnel: fog.tunnel, - watchdogEnabled: fog.watchdogEnabled, - fogTypeId: fog.fogTypeId, - userId: fog.userId + uuid: updatedFog.uuid, + name: updatedFog.name, + location: updatedFog.location, + gpsMode: updatedFog.gpsMode, + latitude: updatedFog.latitude, + longitude: updatedFog.longitude, + description: updatedFog.description, + lastActive: updatedFog.lastActive, + daemonStatus: updatedFog.daemonStatus, + daemonOperatingDuration: updatedFog.daemonOperatingDuration, + daemonLastStart: updatedFog.daemonLastStart, + memoryUsage: updatedFog.memoryUsage, + diskUsage: updatedFog.diskUsage, + cpuUsage: updatedFog.cpuUsage, + memoryViolation: updatedFog.memoryViolation, + diskViolation: updatedFog.diskViolation, + cpuViolation: updatedFog.cpuViolation, + catalogItemStatus: updatedFog.catalogItemStatus, + repositoryCount: updatedFog.repositoryCount, + repositoryStatus: updatedFog.repositoryStatus, + systemTime: updatedFog.systemTime, + lastStatusTime: updatedFog.lastStatusTime, + ipAddress: updatedFog.ipAddress, + processedMessages: updatedFog.processedMessages, + catalogItemMessageCounts: updatedFog.catalogItemMessageCounts, + messageSpeed: updatedFog.messageSpeed, + lastCommandTime: updatedFog.lastCommandTime, + networkInterface: updatedFog.networkInterface, + dockerUrl: updatedFog.dockerUrl, + diskLimit: updatedFog.diskLimit, + diskDirectory: updatedFog.diskDirectory, + memoryLimit: updatedFog.memoryLimit, + cpuLimit: updatedFog.cpuLimit, + logLimit: updatedFog.logLimit, + logDirectory: updatedFog.logDirectory, + bluetoothEnabled: updatedFog.bluetoothEnabled, + abstractedHardwareEnabled: updatedFog.abstractedHardwareEnabled, + logFileCount: updatedFog.logFileCount, + version: updatedFog.version, + isReadyToUpgrade: updatedFog.isReadyToUpgrade, + isReadyToRollback: updatedFog.isReadyToRollback, + statusFrequency: updatedFog.statusFrequency, + changeFrequency: updatedFog.changeFrequency, + deviceScanFrequency: updatedFog.deviceScanFrequency, + tunnel: updatedFog.tunnel, + watchdogEnabled: updatedFog.watchdogEnabled, + fogTypeId: updatedFog.fogTypeId, + userId: updatedFog.userId }; } -async function _getFogList(filters, user, isCli, transaction) { +async function getFogList(filters, user, isCLI, transaction) { await Validator.validate(filters, Validator.schemas.iofogFilters); - const queryFogData = isCli + const queryFogData = isCLI ? {} : {userId: user.id}; let fogs = await FogManager.findAll(queryFogData, transaction); fogs = _filterFogs(fogs, filters); + const response = []; for (const fog of fogs) { - await _updateFogsConnectionStatus(fog, transaction) + const updatedFog = await _updateFogsConnectionStatus(fog, transaction); + response.push(updatedFog); } return { - fogs: fogs + fogs: response } } -async function _generateProvisioningKey(fogData, user, isCli, transaction) { +async function generateProvisioningKey(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogGenerateProvision); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -263,10 +266,10 @@ async function _generateProvisioningKey(fogData, user, isCli, transaction) { } } -async function _setFogVersionCommand(fogVersionData, user, isCli, transaction) { +async function setFogVersionCommand(fogVersionData, user, isCLI, transaction) { await Validator.validate(fogVersionData, Validator.schemas.iofogSetVersionCommand); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogVersionData.uuid} : {uuid: fogVersionData.uuid, userId: user.id}; @@ -280,20 +283,22 @@ async function _setFogVersionCommand(fogVersionData, user, isCli, transaction) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_FOG_NODE_UUID, fogData.uuid)) } - if ((!fog.isReadyToRollback && fogVersionData.versionCommand === 'rollback') - || (!fog.isReadyToUpgrade && fogVersionData.versionCommand === 'upgrade')) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VERSION_COMMAND, fogVersionData.versionCommand)) + if (!fog.isReadyToRollback && fogVersionData.versionCommand === 'rollback') { + throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_ROLLBACK) + } + if (!fog.isReadyToUpgrade && fogVersionData.versionCommand === 'upgrade') { + throw new Errors.ValidationError(ErrorMessages.INVALID_VERSION_COMMAND_UPGRADE) } - await _generateProvisioningKey({uuid: fogVersionData.uuid}, user, isCli, transaction); + await generateProvisioningKey({uuid: fogVersionData.uuid}, user, isCLI, transaction); await FogVersionCommandManager.updateOrCreate({iofogUuid: fogVersionData.uuid}, newVersionCommand, transaction); await ChangeTrackingService.update(fogVersionData.uuid, ChangeTrackingService.events.version, transaction) } -async function _setFogRebootCommand(fogData, user, isCli, transaction) { +async function setFogRebootCommand(fogData, user, isCLI, transaction) { await Validator.validate(fogData, Validator.schemas.iofogReboot); - const queryFogData = isCli + const queryFogData = isCLI ? {uuid: fogData.uuid} : {uuid: fogData.uuid, userId: user.id}; @@ -305,7 +310,7 @@ async function _setFogRebootCommand(fogData, user, isCli, transaction) { await ChangeTrackingService.update(fogData.uuid, ChangeTrackingService.events.reboot, transaction) } -async function _getHalHardwareInfo(uuidObj, user, isCLI, transaction) { +async function getHalHardwareInfo(uuidObj, user, isCLI, transaction) { await Validator.validate(uuidObj, Validator.schemas.halGet); const fog = await FogManager.findOne({ @@ -320,7 +325,7 @@ async function _getHalHardwareInfo(uuidObj, user, isCLI, transaction) { }, transaction); } -async function _getHalUsbInfo(uuidObj, user, isCLI, transaction) { +async function getHalUsbInfo(uuidObj, user, isCLI, transaction) { await Validator.validate(uuidObj, Validator.schemas.halGet); const fog = await FogManager.findOne({ @@ -365,12 +370,16 @@ async function _updateFogsConnectionStatus(fog, transaction) { const minInMs = 60000; const intervalInMs = fog.statusFrequency > minInMs ? fog.statusFrequency * 2 : minInMs; if (fog.daemonStatus !== 'UNKNOWN' && Date.now() - fog.lastStatusTime > intervalInMs) { - fog.daemonStatus = 'UNKNOWN'; - fog.ipAddress = '0.0.0.0'; const queryFogData = {uuid: fog.uuid}; - const toUpdate = {daemonStatus: fog.daemonStatus, ipAddress: fog.ipAddress}; - await FogManager.update(queryFogData, toUpdate, transaction) + const toUpdate = {daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0'}; + await FogManager.update(queryFogData, toUpdate, transaction); + const updatedFog = Object.assign({}, fog); + updatedFog.daemonStatus = 'UNKNOWN'; + updatedFog.ipAddress = '0.0.0.0'; + return updatedFog; } + + return fog; } async function _processDeleteCommand(fog, transaction) { @@ -399,7 +408,7 @@ async function _createHalMicroserviceForFog(fogData, oldFog, user, transaction) await MicroserviceManager.create(halMicroserviceData, transaction); } -async function _deleteHalMicroseviceByFog(fogData, transaction) { +async function _deleteHalMicroserviceByFog(fogData, transaction) { const halItem = await CatalogService.getHalCatalogItem(transaction); const deleteHalMicroserviceData = { iofogUuid: fogData.uuid, @@ -438,15 +447,15 @@ async function _deleteBluetoothMicroserviceByFog(fogData, transaction) { } module.exports = { - createFog: TransactionDecorator.generateTransaction(_createFog), - updateFog: TransactionDecorator.generateTransaction(_updateFog), - deleteFog: TransactionDecorator.generateTransaction(_deleteFog), - getFogWithTransaction: TransactionDecorator.generateTransaction(_getFog), - getFogList: TransactionDecorator.generateTransaction(_getFogList), - generateProvisioningKey: TransactionDecorator.generateTransaction(_generateProvisioningKey), - setFogVersionCommand: TransactionDecorator.generateTransaction(_setFogVersionCommand), - setFogRebootCommand: TransactionDecorator.generateTransaction(_setFogRebootCommand), - getHalHardwareInfo: TransactionDecorator.generateTransaction(_getHalHardwareInfo), - getHalUsbInfo: TransactionDecorator.generateTransaction(_getHalUsbInfo), - getFog: _getFog + createFog: TransactionDecorator.generateTransaction(createFog), + updateFog: TransactionDecorator.generateTransaction(updateFog), + deleteFog: TransactionDecorator.generateTransaction(deleteFog), + getFogWithTransaction: TransactionDecorator.generateTransaction(getFog), + getFogList: TransactionDecorator.generateTransaction(getFogList), + generateProvisioningKey: TransactionDecorator.generateTransaction(generateProvisioningKey), + setFogVersionCommand: TransactionDecorator.generateTransaction(setFogVersionCommand), + setFogRebootCommand: TransactionDecorator.generateTransaction(setFogRebootCommand), + getHalHardwareInfo: TransactionDecorator.generateTransaction(getHalHardwareInfo), + getHalUsbInfo: TransactionDecorator.generateTransaction(getHalUsbInfo), + getFog: getFog }; \ No newline at end of file diff --git a/src/services/microservices-service.js b/src/services/microservices-service.js index 2207b4d04..e707df982 100644 --- a/src/services/microservices-service.js +++ b/src/services/microservices-service.js @@ -11,7 +11,7 @@ * */ -const logger = require('../logger') +const logger = require('../logger'); const TransactionDecorator = require('../decorators/transaction-decorator'); const MicroserviceManager = require('../sequelize/managers/microservice-manager'); const MicroserviceStatusManager = require('../sequelize/managers/microservice-status-manager'); @@ -22,19 +22,20 @@ const ConnectorManager = require('../sequelize/managers/connector-manager'); const ConnectorPortManager = require('../sequelize/managers/connector-port-manager'); const MicroservicePublicModeManager = require('../sequelize/managers/microservice-public-mode-manager'); const ChangeTrackingService = require('./change-tracking-service'); +const ConnectorPortService = require('./connector-port-service'); const IoFogService = require('../services/iofog-service'); const AppHelper = require('../helpers/app-helper'); const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); -const Validation = require('../schemas/index'); -const ConnectorService = require('../services/connector-service'); +const Validator = require('../schemas/index'); const FlowService = require('../services/flow-service'); const CatalogService = require('../services/catalog-service'); const RoutingManager = require('../sequelize/managers/routing-manager'); const Op = require('sequelize').Op; const fs = require('fs'); +const _ = require('underscore'); -async function _listMicroservices(flowId, user, isCLI, transaction) { +async function listMicroservices(flowId, user, isCLI, transaction) { if (!isCLI) { await FlowService.getFlow(flowId, user, isCLI, transaction); } @@ -46,7 +47,7 @@ async function _listMicroservices(flowId, user, isCLI, transaction) { } } -async function _getMicroservice(microserviceUuid, user, isCLI, transaction) { +async function getMicroservice(microserviceUuid, user, isCLI, transaction) { if (!isCLI) { await _validateMicroserviceOnGet(user.id, microserviceUuid, transaction); } @@ -61,14 +62,14 @@ async function _getMicroservice(microserviceUuid, user, isCLI, transaction) { return microservice; } -async function _createMicroserviceOnFog(microserviceData, user, isCLI, transaction) { - await Validation.validate(microserviceData, Validation.schemas.microserviceCreate); +async function createMicroservice(microserviceData, user, isCLI, transaction) { + await Validator.validate(microserviceData, Validator.schemas.microserviceCreate); const microservice = await _createMicroservice(microserviceData, user, isCLI, transaction); if (microserviceData.ports) { for (const port of microserviceData.ports) { - await _createPortMapping(microservice.uuid, port, user, isCLI, transaction); + await createPortMapping(microservice.uuid, port, user, isCLI, transaction); } } if (microserviceData.volumeMappings) { @@ -90,59 +91,8 @@ async function _createMicroserviceOnFog(microserviceData, user, isCLI, transacti } } -async function _createMicroservice(microserviceData, user, isCLI, transaction) { - - let newMicroservice = { - uuid: AppHelper.generateRandomString(32), - name: microserviceData.name, - config: microserviceData.config, - catalogItemId: microserviceData.catalogItemId, - flowId: microserviceData.flowId, - iofogUuid: microserviceData.iofogUuid, - rootHostAccess: microserviceData.rootHostAccess, - logSize: microserviceData.logLimit, - userId: user.id - }; - - newMicroservice = AppHelper.deleteUndefinedFields(newMicroservice); - - await _checkForDuplicateName(newMicroservice.name, {}, user.id, transaction); - - //validate catalog item - await CatalogService.getCatalogItem(newMicroservice.catalogItemId, user, isCLI, transaction); - //validate flow - await FlowService.getFlow(newMicroservice.flowId, user, isCLI, transaction); - //validate fog node - if (newMicroservice.iofogUuid) { - await IoFogService.getFog({uuid: newMicroservice.iofogUuid}, user, isCLI, transaction); - } - - return await MicroserviceManager.create(newMicroservice, transaction); -} - -async function _createMicroserviceStatus(uuid, transaction) { - return await MicroserviceStatusManager.create({ - microserviceUuid: uuid - }, transaction); -} - -async function _createVolumeMappings(volumeMappings, microserviceUuid, transaction) { - - for (let volumeMapping of volumeMappings) { - volumeMapping.microserviceUuid = microserviceUuid - } - - await VolumeMappingManager.bulkCreate(volumeMappings, transaction) -} - -async function _createRoutes(routes, microserviceUuid, user, transaction) { - for (let route of routes) { - await _createRoute(microserviceUuid, route, user, false, transaction) - } -} - -async function _updateMicroservice(microserviceUuid, microserviceData, user, isCLI, transaction) { - await Validation.validate(microserviceData, Validation.schemas.microserviceUpdate); +async function updateMicroservice(microserviceUuid, microserviceData, user, isCLI, transaction) { + await Validator.validate(microserviceData, Validator.schemas.microserviceUpdate); const query = isCLI ? @@ -191,8 +141,8 @@ async function _updateMicroservice(microserviceUuid, microserviceData, user, isC if (microserviceDataUpdate.iofogUuid && microserviceDataUpdate.iofogUuid !== microservice.iofogUuid) { const routes = await _getLogicalNetworkRoutesByFog(microservice.iofogUuid, transaction); for (let route of routes) { - await _deleteRoute(route.sourceMicroserviceUuid, route.destMicroserviceUuid, user, isCLI, transaction); - await _createRoute(route.sourceMicroserviceUuid, route.destMicroserviceUuid, user, isCLI, transaction); + await deleteRoute(route.sourceMicroserviceUuid, route.destMicroserviceUuid, user, isCLI, transaction); + await createRoute(route.sourceMicroserviceUuid, route.destMicroserviceUuid, user, isCLI, transaction); //update change tracking for another fog in route if (microservice.iofogUuid === route.sourceIofogUuid) { await _updateChangeTracking(false, route.destIofogUuid, transaction); @@ -205,27 +155,10 @@ async function _updateMicroservice(microserviceUuid, microserviceData, user, isC } //update change tracking for new fog - await _updateChangeTracking(microserviceData.config ? true : false, microserviceDataUpdate.iofogUuid, transaction); -} - -async function _updateVolumeMappings(volumeMappings, microserviceUuid, transaction) { - for (let volumeMapping of volumeMappings) { - await VolumeMappingManager.update({ - microserviceUuid: microserviceUuid - }, volumeMapping, transaction); - } -}; - -async function _updateChangeTracking(configUpdated, fogNodeUuid, transaction) { - if (configUpdated) { - await ChangeTrackingService.update(fogNodeUuid, ChangeTrackingService.events.microserviceCommon, transaction); - } else { - await ChangeTrackingService.update(fogNodeUuid, ChangeTrackingService.events.microserviceList, transaction); - } + await _updateChangeTracking(!!microserviceData.config, microserviceDataUpdate.iofogUuid, transaction); } -async function _deleteMicroservice(microserviceUuid, microserviceData, user, isCLI, transaction) { - +async function deleteMicroservice(microserviceUuid, microserviceData, user, isCLI, transaction) { const where = isCLI ? { @@ -237,16 +170,13 @@ async function _deleteMicroservice(microserviceUuid, microserviceData, user, isC userId: user.id }; - const microservice = await MicroserviceManager.findOneWithStatus(where, transaction); if (!microservice) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)); } - await _deletePortMappings(microservice, user, transaction); - if (microservice.microserviceStatus.status === MicroserviceStates.NOT_RUNNING) { - await _deleteMicroserviceWithRoutes(microserviceUuid, transaction); + await _deleteMicroserviceWithRoutesAndPortMappings(microserviceUuid, transaction); } else { await MicroserviceManager.update({ uuid: microserviceUuid @@ -260,62 +190,15 @@ async function _deleteMicroservice(microserviceUuid, microserviceData, user, isC await _updateChangeTracking(false, microservice.iofogUuid, transaction) } -async function _deletePortMappings(microservice, user, transaction) { - const msPortMappings = await MicroservicePortManager.findAll({ - microserviceUuid: microservice.uuid - }, transaction); - - for (const msPorts of msPortMappings) { - if (msPorts.isPublic) { - await _deletePortMappingOverConnector(microservice, msPorts, user, transaction) - } else { - await _deleteSimplePortMapping(microservice, msPorts, user, transaction) - } - } -} - -async function _deleteNotRunningMicroservices(transaction) { +async function deleteNotRunningMicroservices(transaction) { const microservices = await MicroserviceManager.findAllWithStatuses(transaction); microservices .filter(microservice => microservice.delete) .filter(microservice => microservice.microserviceStatus.status === MicroserviceStates.NOT_RUNNING) - .forEach(microservice => _deleteMicroserviceWithRoutes(microservice.uuid, transaction)); -} - -async function _checkForDuplicateName(name, item, userId, transaction) { - if (name) { - const where = item.id - ? - { - name: name, - uuid: {[Op.ne]: item.id}, - userId: userId - } - : - { - name: name, - userId: userId - }; - - const result = await MicroserviceManager.findOne(where, transaction); - if (result) { - throw new Errors.DuplicatePropertyError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, name)); - } - } -} - -async function _validateMicroserviceOnGet(userId, microserviceUuid, transaction) { - const where = { - '$flow.user.id$': userId, - uuid: microserviceUuid - }; - const microservice = await MicroserviceManager.findMicroserviceOnGet(where, transaction); - if (!microservice) { - throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER); - } + .forEach(microservice => _deleteMicroserviceWithRoutesAndPortMappings(microservice.uuid, transaction)); } -async function _createRoute(sourceMicroserviceUuid, destMicroserviceUuid, user, isCLI, transaction) { +async function createRoute(sourceMicroserviceUuid, destMicroserviceUuid, user, isCLI, transaction) { const sourceWhere = isCLI ? {uuid: sourceMicroserviceUuid} : {uuid: sourceMicroserviceUuid, userId: user.id}; @@ -344,15 +227,352 @@ async function _createRoute(sourceMicroserviceUuid, destMicroserviceUuid, user, const route = await RoutingManager.findOne({ sourceMicroserviceUuid: sourceMicroserviceUuid, destMicroserviceUuid: destMicroserviceUuid - }, transaction) + }, transaction); if (route) { throw new Errors.ValidationError('route already exists') } - if (sourceMicroservice.iofogUuid === destMicroservice.iofogUuid) { - await _createSimpleRoute(sourceMicroservice, destMicroservice, transaction) - } else { - await _createRouteOverConnector(sourceMicroservice, destMicroservice, user, transaction) + if (sourceMicroservice.iofogUuid === destMicroservice.iofogUuid) { + await _createSimpleRoute(sourceMicroservice, destMicroservice, transaction) + } else { + await _createRouteOverConnector(sourceMicroservice, destMicroservice, user, transaction) + } +} + +async function updateRouteOverConnector(connector, transaction) { + const routes = await RoutingManager.findAllRoutesByConnectorId(connector.id, transaction); + const networkMicroserviceUuids = _.flatten(_.map( + routes, route => [route.sourceNetworkMicroserviceUuid, route.destNetworkMicroserviceUuid] + )); + await _updateNetworkMicroserviceConfigs(networkMicroserviceUuids, connector, transaction); +} + +async function deleteRoute(sourceMicroserviceUuid, destMicroserviceUuid, user, isCLI, transaction) { + const sourceWhere = isCLI + ? {uuid: sourceMicroserviceUuid} + : {uuid: sourceMicroserviceUuid, userId: user.id}; + + const sourceMicroservice = await MicroserviceManager.findOne(sourceWhere, transaction); + if (!sourceMicroservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, sourceMicroserviceUuid)) + } + + const destWhere = isCLI + ? {uuid: destMicroserviceUuid} + : {uuid: destMicroserviceUuid, userId: user.id}; + + const destMicroservice = await MicroserviceManager.findOne(destWhere, transaction); + if (!destMicroservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, destMicroserviceUuid)) + } + + const route = await RoutingManager.findOne({ + sourceMicroserviceUuid: sourceMicroserviceUuid, + destMicroserviceUuid: destMicroserviceUuid + }, transaction); + if (!route) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.ROUTE_NOT_FOUND)) + } + + if (route.isNetworkConnection) { + await _deleteRouteOverConnector(route, transaction) + } else { + await _deleteSimpleRoute(route, transaction) + } +} + +async function createPortMapping(microserviceUuid, portMappingData, user, isCLI, transaction) { + await Validator.validate(portMappingData, Validator.schemas.portsCreate); + await _validatePorts(portMappingData.internal, portMappingData.external); + + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + + const microservice = await MicroserviceManager.findOne(where, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + if (!microservice.iofogUuid) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)); + } + + const msPorts = await MicroservicePortManager.findOne({ + microserviceUuid: microserviceUuid, + [Op.or]: + [ + { + portInternal: portMappingData.internal + }, + { + portExternal: portMappingData.external + } + ] + }, transaction); + if (msPorts) { + throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_ALREADY_EXISTS); + } + + if (portMappingData.publicMode) { + return await _createPortMappingOverConnector(microservice, portMappingData, user, transaction) + } else { + return await _createSimplePortMapping(microservice, portMappingData, user, transaction) + } +} + +async function updatePortMappingOverConnector(connector, transaction) { + const microservicePublicModes = await MicroservicePublicModeManager.findAllMicroservicePublicModesByConnectorId(connector.id, transaction); + const networkMicroserviceUuids = microservicePublicModes.map(obj => obj.networkMicroserviceUuid); + await _updateNetworkMicroserviceConfigs(networkMicroserviceUuids, connector, transaction); +} + +async function deletePortMapping(microserviceUuid, internalPort, user, isCLI, transaction) { + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + + const microservice = await MicroserviceManager.findOne(where, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const msPorts = await MicroservicePortManager.findOne({ + microserviceUuid: microserviceUuid, + portInternal: internalPort + }, transaction) + if (!msPorts) { + throw new Errors.NotFoundError('port mapping not exists') + } + + if (msPorts.isPublic) { + await _deletePortMappingOverConnector(microservice, msPorts, user, transaction) + } else { + await _deleteSimplePortMapping(microservice, msPorts, user, transaction) + } +} + +async function listPortMappings(microserviceUuid, user, isCLI, transaction) { + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + const microservice = await MicroserviceManager.findOne(where, transaction) + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const portsPairs = await MicroservicePortManager.findAll({microserviceUuid: microserviceUuid}, transaction) + return await _buildPortsList(portsPairs, transaction) +} + +async function getPhysicalConnections(microservice, transaction) { + let res = []; + const pubModes = await MicroservicePublicModeManager.findAll({microserviceUuid: microservice.uuid}, transaction) + for (const pm of pubModes) { + res.push(pm.networkMicroserviceUuid) + } + + const sourceRoutes = await RoutingManager.findAll({sourceMicroserviceUuid: microservice.uuid}, transaction) + for (const sr of sourceRoutes) { + if (!sr.sourceIofogUuid || !sr.destIofogUuid) { + continue; + } else if (sr.sourceIofogUuid === sr.destIofogUuid) { + res.push(sr.destMicroserviceUuid) + } else if (sr.sourceIofogUuid !== sr.destIofogUuid) { + res.push(sr.sourceNetworkMicroserviceUuid) + } + } + + const netwRoutes = await RoutingManager.findAll({destNetworkMicroserviceUuid: microservice.uuid}, transaction) + for (const nr of netwRoutes) { + res.push(nr.destMicroserviceUuid) + } + + return res +} + +async function createVolumeMapping(microserviceUuid, volumeMappingData, user, isCLI, transaction) { + await Validator.validate(volumeMappingData, Validator.schemas.volumeMappings); + + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + + const microservice = await MicroserviceManager.findOne(where, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const volueMapping = await VolumeMappingManager.findOne({ + microserviceUuid: microserviceUuid, + hostDestination: volumeMappingData.hostDestination, + containerDestination: volumeMappingData.containerDestination + }, transaction); + if (volueMapping) { + throw new Errors.ValidationError(ErrorMessages.VOLUME_MAPPING_ALREADY_EXISTS); + } + + const volumeMappingObj = { + microserviceUuid: microserviceUuid, + hostDestination: volumeMappingData.hostDestination, + containerDestination: volumeMappingData.containerDestination, + accessMode: volumeMappingData.accessMode + }; + + return await VolumeMappingManager.create(volumeMappingObj, transaction); +} + +async function deleteVolumeMapping(microserviceUuid, volumeMappingUuid, user, isCLI, transaction) { + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + + const microservice = await MicroserviceManager.findOne(where, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const volumeMappingWhere = { + uuid: volumeMappingUuid, + microserviceUuid: microserviceUuid + }; + + const affectedRows = await VolumeMappingManager.delete(volumeMappingWhere, transaction); + if (affectedRows === 0) { + throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VOLUME_MAPPING_UUID, volumeMappingUuid)); + } +} + +async function listVolumeMappings(microserviceUuid, user, isCLI, transaction) { + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + const microservice = await MicroserviceManager.findOne(where, transaction); + if (!microservice) { + throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) + } + + const volumeMappingWhere = { + microserviceUuid: microserviceUuid + }; + return await VolumeMappingManager.findAll(volumeMappingWhere, transaction); +} + +async function _createMicroservice(microserviceData, user, isCLI, transaction) { + + let newMicroservice = { + uuid: AppHelper.generateRandomString(32), + name: microserviceData.name, + config: microserviceData.config, + catalogItemId: microserviceData.catalogItemId, + flowId: microserviceData.flowId, + iofogUuid: microserviceData.iofogUuid, + rootHostAccess: microserviceData.rootHostAccess, + logSize: microserviceData.logLimit, + userId: user.id + }; + + newMicroservice = AppHelper.deleteUndefinedFields(newMicroservice); + + await _checkForDuplicateName(newMicroservice.name, {}, user.id, transaction); + + //validate catalog item + await CatalogService.getCatalogItem(newMicroservice.catalogItemId, user, isCLI, transaction); + //validate flow + await FlowService.getFlow(newMicroservice.flowId, user, isCLI, transaction); + //validate fog node + if (newMicroservice.iofogUuid) { + await IoFogService.getFog({uuid: newMicroservice.iofogUuid}, user, isCLI, transaction); + } + + return await MicroserviceManager.create(newMicroservice, transaction); +} + +async function _createMicroserviceStatus(uuid, transaction) { + return await MicroserviceStatusManager.create({ + microserviceUuid: uuid + }, transaction); +} + +async function _createVolumeMappings(volumeMappings, microserviceUuid, transaction) { + + const mappings = []; + for (const volumeMapping of volumeMappings) { + const mapping = Object.assign({}, volumeMapping); + mapping.microserviceUuid = microserviceUuid; + mappings.push(mapping); + } + + await VolumeMappingManager.bulkCreate(mappings, transaction) +} + +async function _createRoutes(routes, microserviceUuid, user, transaction) { + for (const route of routes) { + await createRoute(microserviceUuid, route, user, false, transaction) + } +} + +async function _updateVolumeMappings(volumeMappings, microserviceUuid, transaction) { + for (let volumeMapping of volumeMappings) { + await VolumeMappingManager.update({ + microserviceUuid: microserviceUuid + }, volumeMapping, transaction); + } +} + +async function _updateChangeTracking(configUpdated, fogNodeUuid, transaction) { + if (configUpdated) { + await ChangeTrackingService.update(fogNodeUuid, ChangeTrackingService.events.microserviceCommon, transaction); + } else { + await ChangeTrackingService.update(fogNodeUuid, ChangeTrackingService.events.microserviceList, transaction); + } +} + +//TODO use in _deleteMicroserviceWithRoutesAndPortMappings +async function _deletePortMappings(microservice, user, transaction) { + const msPortMappings = await MicroservicePortManager.findAll({ + microserviceUuid: microservice.uuid + }, transaction); + + for (const msPorts of msPortMappings) { + if (msPorts.isPublic) { + await _deletePortMappingOverConnector(microservice, msPorts, user, transaction) + } else { + await _deleteSimplePortMapping(microservice, msPorts, user, transaction) + } + } +} + +async function _checkForDuplicateName(name, item, userId, transaction) { + if (name) { + const where = item.id + ? + { + name: name, + uuid: {[Op.ne]: item.id}, + userId: userId + } + : + { + name: name, + userId: userId + }; + + const result = await MicroserviceManager.findOne(where, transaction); + if (result) { + throw new Errors.DuplicatePropertyError(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, name)); + } + } +} + +async function _validateMicroserviceOnGet(userId, microserviceUuid, transaction) { + const where = { + '$flow.user.id$': userId, + uuid: microserviceUuid + }; + const microservice = await MicroserviceManager.findMicroserviceOnGet(where, transaction); + if (!microservice) { + throw new Errors.NotFoundError(ErrorMessages.INVALID_MICROSERVICE_USER); } } @@ -373,18 +593,52 @@ async function _createSimpleRoute(sourceMicroservice, destMicroservice, transact destMicroserviceUuid: destMicroservice.uuid, sourceIofogUuid: sourceMicroservice.iofogUuid, destIofogUuid: destMicroservice.iofogUuid //same as sourceIofogUuid - } + }; - await RoutingManager.create(routeData, transaction) + await RoutingManager.create(routeData, transaction); await _switchOnUpdateFlagsForMicroservicesInRoute(sourceMicroservice, destMicroservice, transaction) } + +async function _updateNetworkMicroserviceConfigs(networkMicroserviceUuids, connector, transaction) { + const microservices = await MicroserviceManager.findAll({uuid: networkMicroserviceUuids}, transaction); + + let cert; + if (!connector.devMode && connector.cert) { + cert = AppHelper.trimCertificate(fs.readFileSync(connector.cert, "utf-8")) + } + + for (const microservice of microservices) { + const msConfig = JSON.parse(microservice.config); + msConfig.host = connector.domain; + msConfig.cert = cert; + msConfig.devmode = connector.devMode; + const newConfig = { + config: JSON.stringify(msConfig), + rebuild: true + }; + await MicroserviceManager.update({ + uuid: microservice.uuid + }, newConfig, transaction); + } + + const onlyUnique = (value, index, self) => self.indexOf(value) === index; + const iofogUuids = microservices + .map(obj => obj.iofogUuid) + .filter(onlyUnique) + .filter(val => val !== null); + + for (const iofogUuid of iofogUuids) { + await ChangeTrackingService.update(iofogUuid, ChangeTrackingService.events.microserviceCommon, transaction); + } +} + async function _createRouteOverConnector(sourceMicroservice, destMicroservice, user, transaction) { - //open comsat - const justOpenedConnectorsPorts = await ConnectorService.openPortOnRandomConnector(false, transaction) + //open connector + const justOpenedConnectorsPorts = await ConnectorPortService.openPortOnRandomConnector(false, transaction); - const ports = justOpenedConnectorsPorts.ports - const connector = justOpenedConnectorsPorts.connector + const ports = justOpenedConnectorsPorts.ports; + const connector = justOpenedConnectorsPorts.connector; const createConnectorPortData = { port1: ports.port1, @@ -398,9 +652,9 @@ async function _createRouteOverConnector(sourceMicroservice, destMicroservice, u connectorId: ports.connectorId, mappingId: ports.id }; - const connectorPort = await ConnectorPortManager.create(createConnectorPortData, transaction) + const connectorPort = await ConnectorPortManager.create(createConnectorPortData, transaction); - const networkCatalogItem = await CatalogService.getNetworkCatalogItem(transaction) + const networkCatalogItem = await CatalogService.getNetworkCatalogItem(transaction); let cert; if (!connector.devMode && connector.cert) { @@ -420,7 +674,7 @@ async function _createRouteOverConnector(sourceMicroservice, destMicroservice, u 'heartbeatfrequency': 20000, 'heartbeatabsencethreshold': 60000, 'devmode': connector.devMode - } + }; const sourceNetworkMicroservice = await _createNetworkMicroserviceForMaster( sourceMicroservice, sourceNetwMsConfig, @@ -442,7 +696,7 @@ async function _createRouteOverConnector(sourceMicroservice, destMicroservice, u 'heartbeatfrequency': 20000, 'heartbeatabsencethreshold': 60000, 'devmode': connector.devMode - } + }; const destNetworkMicroservice = await _createNetworkMicroserviceForMaster( destMicroservice, destNetwMsConfig, @@ -461,8 +715,8 @@ async function _createRouteOverConnector(sourceMicroservice, destMicroservice, u sourceNetworkMicroserviceUuid: sourceNetworkMicroservice.uuid, destNetworkMicroserviceUuid: destNetworkMicroservice.uuid, connectorPortId: connectorPort.id - } - await RoutingManager.create(routeData, transaction) + }; + await RoutingManager.create(routeData, transaction); await _switchOnUpdateFlagsForMicroservicesInRoute(sourceMicroservice, destMicroservice, transaction) } @@ -488,112 +742,38 @@ async function _createNetworkMicroserviceForMaster(masterMicroservice, sourceNet async function _switchOnUpdateFlagsForMicroservicesInRoute(sourceMicroservice, destMicroservice, transaction) { const updateRebuildMs = { rebuild: true - } - await MicroserviceManager.update({uuid: sourceMicroservice.uuid}, updateRebuildMs, transaction) - await MicroserviceManager.update({uuid: destMicroservice.uuid}, updateRebuildMs, transaction) - - await ChangeTrackingService.update(sourceMicroservice.iofogUuid, ChangeTrackingService.events.microserviceFull, transaction) - await ChangeTrackingService.update(destMicroservice.iofogUuid, ChangeTrackingService.events.microserviceFull, transaction) -} - -async function _deleteRoute(sourceMicroserviceUuid, destMicroserviceUuid, user, isCLI, transaction) { - const sourceWhere = isCLI - ? {uuid: sourceMicroserviceUuid} - : {uuid: sourceMicroserviceUuid, userId: user.id}; - - const sourceMicroservice = await MicroserviceManager.findOne(sourceWhere, transaction); - if (!sourceMicroservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, sourceMicroserviceUuid)) - } - - const destWhere = isCLI - ? {uuid: destMicroserviceUuid} - : {uuid: destMicroserviceUuid, userId: user.id}; - - const destMicroservice = await MicroserviceManager.findOne(destWhere, transaction); - if (!destMicroservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, destMicroserviceUuid)) - } - - const route = await RoutingManager.findOne({ - sourceMicroserviceUuid: sourceMicroserviceUuid, - destMicroserviceUuid: destMicroserviceUuid - }, transaction) - if (!route) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.ROUTE_NOT_FOUND)) - } + }; + await MicroserviceManager.update({uuid: sourceMicroservice.uuid}, updateRebuildMs, transaction); + await MicroserviceManager.update({uuid: destMicroservice.uuid}, updateRebuildMs, transaction); - if (route.isNetworkConnection) { - await _deleteRouteOverConnector(route, transaction) - } else { - await _deleteSimpleRoute(route, transaction) - } + await ChangeTrackingService.update(sourceMicroservice.iofogUuid, ChangeTrackingService.events.microserviceFull, transaction); + await ChangeTrackingService.update(destMicroservice.iofogUuid, ChangeTrackingService.events.microserviceFull, transaction); } async function _deleteSimpleRoute(route, transaction) { - await RoutingManager.delete({id: route.id}, transaction) + await RoutingManager.delete({id: route.id}, transaction); - await ChangeTrackingService.update(route.sourceIofogUuid, ChangeTrackingService.events.microserviceRouting, transaction) - await ChangeTrackingService.update(route.destIofogUuid, ChangeTrackingService.events.microserviceRouting, transaction) + await ChangeTrackingService.update(route.sourceIofogUuid, ChangeTrackingService.events.microserviceRouting, transaction); + await ChangeTrackingService.update(route.destIofogUuid, ChangeTrackingService.events.microserviceRouting, transaction); } async function _deleteRouteOverConnector(route, transaction) { - const ports = await ConnectorPortManager.findOne({id: route.connectorPortId}, transaction) - const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction) + const ports = await ConnectorPortManager.findOne({id: route.connectorPortId}, transaction); + const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction); try { - await ConnectorService.closePortOnConnector(connector, ports, transaction); + await ConnectorPortService.closePortOnConnector(connector, ports); } catch (e) { logger.warn(`Can't close ports pair ${ports.mappingId} on connector ${connector.publicIp}. Delete manually if necessary`); } - await RoutingManager.delete({id: route.id}, transaction) - await ConnectorPortManager.delete({id: ports.id}, transaction) - await MicroserviceManager.delete({uuid: route.sourceNetworkMicroserviceUuid}, transaction) - await MicroserviceManager.delete({uuid: route.destNetworkMicroserviceUuid}, transaction) - - await ChangeTrackingService.update(route.sourceIofogUuid, ChangeTrackingService.events.microserviceFull, transaction) - await ChangeTrackingService.update(route.destIofogUuid, ChangeTrackingService.events.microserviceFull, transaction) -} - -async function _createPortMapping(microserviceUuid, portMappingData, user, isCLI, transaction) { - await Validation.validate(portMappingData, Validation.schemas.portsCreate); - await _validatePorts(portMappingData.internal, portMappingData.external) - - const where = isCLI - ? {uuid: microserviceUuid} - : {uuid: microserviceUuid, userId: user.id}; - - const microservice = await MicroserviceManager.findOne(where, transaction) - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - if (!microservice.iofogUuid) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.REQUIRED_FOG_NODE)); - } - - const msPorts = await MicroservicePortManager.findOne({ - microserviceUuid: microserviceUuid, - [Op.or]: - [ - { - portInternal: portMappingData.internal - }, - { - portExternal: portMappingData.external - } - ] - }, transaction); - if (msPorts) { - throw new Errors.ValidationError(ErrorMessages.PORT_MAPPING_ALREADY_EXISTS); - } + await RoutingManager.delete({id: route.id}, transaction); + await ConnectorPortManager.delete({id: ports.id}, transaction); + await MicroserviceManager.delete({uuid: route.sourceNetworkMicroserviceUuid}, transaction); + await MicroserviceManager.delete({uuid: route.destNetworkMicroserviceUuid}, transaction); - if (portMappingData.publicMode) { - return await _createPortMappingOverConnector(microservice, portMappingData, user, transaction) - } else { - return await _createSimplePortMapping(microservice, portMappingData, user, transaction) - } + await ChangeTrackingService.update(route.sourceIofogUuid, ChangeTrackingService.events.microserviceFull, transaction); + await ChangeTrackingService.update(route.destIofogUuid, ChangeTrackingService.events.microserviceFull, transaction); } async function _createSimplePortMapping(microservice, portMappingData, user, transaction) { @@ -604,18 +784,19 @@ async function _createSimplePortMapping(microservice, portMappingData, user, tra portExternal: portMappingData.external, userId: microservice.userId, microserviceUuid: microservice.uuid - } + }; - await MicroservicePortManager.create(mappingData, transaction) + await MicroservicePortManager.create(mappingData, transaction); await _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, false, transaction) } + async function _createPortMappingOverConnector(microservice, portMappingData, user, transaction) { - //open comsat - const justOpenedConnectorsPorts = await ConnectorService.openPortOnRandomConnector(true, transaction) + //open connector + const justOpenedConnectorsPorts = await ConnectorPortService.openPortOnRandomConnector(true, transaction); - const ports = justOpenedConnectorsPorts.ports - const connector = justOpenedConnectorsPorts.connector + const ports = justOpenedConnectorsPorts.ports; + const connector = justOpenedConnectorsPorts.connector; const createConnectorPortData = { port1: ports.port1, @@ -629,9 +810,9 @@ async function _createPortMappingOverConnector(microservice, portMappingData, us connectorId: ports.connectorId, mappingId: ports.id }; - const connectorPort = await ConnectorPortManager.create(createConnectorPortData, transaction) + const connectorPort = await ConnectorPortManager.create(createConnectorPortData, transaction); - const networkCatalogItem = await CatalogService.getNetworkCatalogItem(transaction) + const networkCatalogItem = await CatalogService.getNetworkCatalogItem(transaction); let cert; if (!connector.devMode && connector.cert) { @@ -650,7 +831,7 @@ async function _createPortMappingOverConnector(microservice, portMappingData, us 'heartbeatfrequency': 20000, 'heartbeatabsencethreshold': 60000, 'devmode': connector.devMode - } + }; const networkMicroservice = await _createNetworkMicroserviceForMaster( microservice, netwMsConfig, @@ -666,9 +847,9 @@ async function _createPortMappingOverConnector(microservice, portMappingData, us portExternal: portMappingData.external, userId: microservice.userId, microserviceUuid: microservice.uuid - } + }; - const msPortMapping = await MicroservicePortManager.create(mappingData, transaction) + const msPortMapping = await MicroservicePortManager.create(mappingData, transaction); const msPubModeData = { microserviceUuid: microservice.uuid, @@ -676,11 +857,11 @@ async function _createPortMappingOverConnector(microservice, portMappingData, us iofogUuid: microservice.iofogUuid, microservicePortId: msPortMapping.id, connectorPortId: connectorPort.id - } - await MicroservicePublicModeManager.create(msPubModeData, transaction) + }; + await MicroservicePublicModeManager.create(msPubModeData, transaction); - await _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, true, transaction) + await _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, true, transaction); const publicLink = await _buildLink(connector.devMode ? 'http' : 'https', connector.publicIp, connectorPort.port2) return {publicLink: publicLink} } @@ -688,10 +869,9 @@ async function _createPortMappingOverConnector(microservice, portMappingData, us async function _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, isPublic, transaction) { const updateRebuildMs = { rebuild: true - } - await MicroserviceManager.update({uuid: microservice.uuid}, updateRebuildMs, transaction) + }; + await MicroserviceManager.update({uuid: microservice.uuid}, updateRebuildMs, transaction); - let updateChangeTrackingData = {} if (isPublic) { await ChangeTrackingService.update(microservice.iofogUuid, ChangeTrackingService.events.microserviceFull, transaction) } else { @@ -699,30 +879,6 @@ async function _switchOnUpdateFlagsForMicroservicesForPortMapping(microservice, } } -async function _deletePortMapping(microserviceUuid, internalPort, user, isCLI, transaction) { - const where = isCLI - ? {uuid: microserviceUuid} - : {uuid: microserviceUuid, userId: user.id} - - const microservice = await MicroserviceManager.findOne(where, transaction); - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - const msPorts = await MicroservicePortManager.findOne({ - microserviceUuid: microserviceUuid, - portInternal: internalPort - }, transaction) - if (!msPorts) { - throw new Errors.NotFoundError('port mapping not exists') - } - - if (msPorts.isPublic) { - await _deletePortMappingOverConnector(microservice, msPorts, user, transaction) - } else { - await _deleteSimplePortMapping(microservice, msPorts, user, transaction) - } -} async function _deleteSimplePortMapping(microservice, msPorts, user, transaction) { await MicroservicePortManager.delete({id: msPorts.id}, transaction); @@ -741,7 +897,7 @@ async function _deletePortMappingOverConnector(microservice, msPorts, user, tran const connector = await ConnectorManager.findOne({id: ports.connectorId}, transaction); try { - await ConnectorService.closePortOnConnector(connector, ports, transaction); + await ConnectorPortService.closePortOnConnector(connector, ports); } catch (e) { logger.warn(`Can't close ports pair ${ports.mappingId} on connector ${connector.publicIp}. Delete manually if necessary`); } @@ -764,22 +920,22 @@ async function _validatePorts(internal, external) { //TODO find this ports in project. check is possible to delete some of them || external === 60400 || external === 60401 || external === 10500 || external === 54321 || external === 55555) { - throw new Errors.ValidationError('incorrect port') + throw new Errors.ValidationError('Incorrect port') } } async function _buildPortsList(portsPairs, transaction) { - const res = [] + const res = []; for (const ports of portsPairs) { let portMappingResposeData = { internal: ports.portInternal, external: ports.portExternal, publicMode: ports.isPublic - } + }; if (ports.isPublic) { - const pubMode = await MicroservicePublicModeManager.findOne({microservicePortId: ports.id}, transaction) - const connectorPorts = await ConnectorPortManager.findOne({id: pubMode.connectorPortId}, transaction) - const connector = await ConnectorManager.findOne({id: connectorPorts.connectorId}, transaction) + const pubMode = await MicroservicePublicModeManager.findOne({microservicePortId: ports.id}, transaction); + const connectorPorts = await ConnectorPortManager.findOne({id: pubMode.connectorPortId}, transaction); + const connector = await ConnectorManager.findOne({id: connectorPorts.connectorId}, transaction); portMappingResposeData.publicLink = await _buildLink(connector.devMode ? 'http' : 'https', connector.publicIp, connectorPorts.port2) } @@ -788,45 +944,6 @@ async function _buildPortsList(portsPairs, transaction) { return res } -async function _listPortMappings(microserviceUuid, user, isCLI, transaction) { - const where = isCLI - ? {uuid: microserviceUuid} - : {uuid: microserviceUuid, userId: user.id}; - const microservice = await MicroserviceManager.findOne(where, transaction) - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - const portsPairs = await MicroservicePortManager.findAll({microserviceUuid: microserviceUuid}, transaction) - return await _buildPortsList(portsPairs, transaction) -} - -async function getPhysicalConections(microservice, transaction) { - let res = [] - const pubModes = await MicroservicePublicModeManager.findAll({microserviceUuid: microservice.uuid}, transaction) - for (const pm of pubModes) { - res.push(pm.networkMicroserviceUuid) - } - - const sourceRoutes = await RoutingManager.findAll({sourceMicroserviceUuid: microservice.uuid}, transaction) - for (const sr of sourceRoutes) { - if (!sr.sourceIofogUuid || !sr.destIofogUuid) { - continue; - } else if (sr.sourceIofogUuid === sr.destIofogUuid) { - res.push(sr.destMicroserviceUuid) - } else if (sr.sourceIofogUuid !== sr.destIofogUuid) { - res.push(sr.sourceNetworkMicroserviceUuid) - } - } - - const netwRoutes = await RoutingManager.findAll({destNetworkMicroserviceUuid: microservice.uuid}, transaction) - for (const nr of netwRoutes) { - res.push(nr.destMicroserviceUuid) - } - - return res -} - async function _getLogicalNetworkRoutesByFog(iofogUuid, transaction) { let res = []; const query = { @@ -862,7 +979,7 @@ async function _getLogicalRoutesByMicroservice(microserviceUuid, transaction) { } ] }; - const routes = await RoutingManager.findAll(query, transaction) + const routes = await RoutingManager.findAll(query, transaction); for (let route of routes) { if (route.sourceMicroserviceUuid && route.destMicroserviceUuid) { res.push(route); @@ -871,7 +988,7 @@ async function _getLogicalRoutesByMicroservice(microserviceUuid, transaction) { return res; } -async function _deleteMicroserviceWithRoutes(microserviceUuid, transaction) { +async function _deleteMicroserviceWithRoutesAndPortMappings(microserviceUuid, transaction) { const routes = await _getLogicalRoutesByMicroservice(microserviceUuid, transaction); for (let route of routes) { //TODO: simplify after splitting all endpoints service functions to validation and request processing part @@ -881,7 +998,18 @@ async function _deleteMicroserviceWithRoutes(microserviceUuid, transaction) { const user = { id: userId }; - await _deleteRoute(route.sourceMicroserviceUuid, route.destMicroserviceUuid, user, false, transaction); + await deleteRoute(route.sourceMicroserviceUuid, route.destMicroserviceUuid, user, false, transaction); + } + + const portMappings = await MicroservicePortManager.findAll({microserviceUuid: microserviceUuid}, transaction); + for (const ports of portMappings) { + const userId = (await MicroserviceManager + .findOne({uuid: ports.microserviceUuid}, transaction)) + .userId; + const user = { + id: userId + }; + await deletePortMapping(ports.microserviceUuid, ports.portInternal, user, false, transaction); } await MicroserviceManager.delete({ uuid: microserviceUuid @@ -892,87 +1020,22 @@ async function _buildLink(protocol, ip, port) { return `${protocol}://${ip}:${port}` } -async function _createVolumeMapping(microserviceUuid, volumeMappingData, user, isCLI, transaction) { - await Validation.validate(volumeMappingData, Validation.schemas.volumeMappings); - - const where = isCLI - ? {uuid: microserviceUuid} - : {uuid: microserviceUuid, userId: user.id}; - - const microservice = await MicroserviceManager.findOne(where, transaction); - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - const volueMapping = await VolumeMappingManager.findOne({ - microserviceUuid: microserviceUuid, - hostDestination: volumeMappingData.hostDestination, - containerDestination: volumeMappingData.containerDestination - }, transaction); - if (volueMapping) { - throw new Errors.ValidationError(ErrorMessages.VOLUME_MAPPING_ALREADY_EXISTS); - } - - const volumeMappingObj = { - microserviceUuid: microserviceUuid, - hostDestination: volumeMappingData.hostDestination, - containerDestination: volumeMappingData.containerDestination, - accessMode: volumeMappingData.accessMode - }; - - return await VolumeMappingManager.create(volumeMappingObj, transaction); -} - -async function _deleteVolumeMapping(microserviceUuid, volumeMappingUuid, user, isCLI, transaction) { - const where = isCLI - ? {uuid: microserviceUuid} - : {uuid: microserviceUuid, userId: user.id}; - - const microservice = await MicroserviceManager.findOne(where, transaction); - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - const volumeMappingWhere = { - uuid: volumeMappingUuid, - microserviceUuid: microserviceUuid - }; - - const affectedRows = await VolumeMappingManager.delete(volumeMappingWhere, transaction); - if (affectedRows === 0) { - throw new Errors.ValidationError(AppHelper.formatMessage(ErrorMessages.INVALID_VOLUME_MAPPING_UUID, volumeMappingUuid)); - } -} - -async function _listVolumeMappings(microserviceUuid, user, isCLI, transaction) { - const where = isCLI - ? {uuid: microserviceUuid} - : {uuid: microserviceUuid, userId: user.id}; - const microservice = await MicroserviceManager.findOne(where, transaction) - if (!microservice) { - throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_MICROSERVICE_UUID, microserviceUuid)) - } - - const volumeMappingWhere = { - microserviceUuid: microserviceUuid - }; - return await VolumeMappingManager.findAll(volumeMappingWhere, transaction); -} - module.exports = { - createMicroserviceOnFog: TransactionDecorator.generateTransaction(_createMicroserviceOnFog), - listMicroservices: TransactionDecorator.generateTransaction(_listMicroservices), - getMicroservice: TransactionDecorator.generateTransaction(_getMicroservice), - updateMicroservice: TransactionDecorator.generateTransaction(_updateMicroservice), - deleteMicroservice: TransactionDecorator.generateTransaction(_deleteMicroservice), - createRoute: TransactionDecorator.generateTransaction(_createRoute), - deleteRoute: TransactionDecorator.generateTransaction(_deleteRoute), - createPortMapping: TransactionDecorator.generateTransaction(_createPortMapping), - listMicroservicePortMappings: TransactionDecorator.generateTransaction(_listPortMappings), - deletePortMapping: TransactionDecorator.generateTransaction(_deletePortMapping), - createVolumeMapping: TransactionDecorator.generateTransaction(_createVolumeMapping), - deleteVolumeMapping: TransactionDecorator.generateTransaction(_deleteVolumeMapping), - listVolumeMappings: TransactionDecorator.generateTransaction(_listVolumeMappings), - getPhysicalConnections: getPhysicalConections, - deleteNotRunningMicroservices: _deleteNotRunningMicroservices + createMicroservice: TransactionDecorator.generateTransaction(createMicroservice), + listMicroservices: TransactionDecorator.generateTransaction(listMicroservices), + getMicroservice: TransactionDecorator.generateTransaction(getMicroservice), + updateMicroservice: TransactionDecorator.generateTransaction(updateMicroservice), + deleteMicroservice: TransactionDecorator.generateTransaction(deleteMicroservice), + createRoute: TransactionDecorator.generateTransaction(createRoute), + deleteRoute: TransactionDecorator.generateTransaction(deleteRoute), + createPortMapping: TransactionDecorator.generateTransaction(createPortMapping), + listMicroservicePortMappings: TransactionDecorator.generateTransaction(listPortMappings), + deletePortMapping: TransactionDecorator.generateTransaction(deletePortMapping), + createVolumeMapping: TransactionDecorator.generateTransaction(createVolumeMapping), + deleteVolumeMapping: TransactionDecorator.generateTransaction(deleteVolumeMapping), + listVolumeMappings: TransactionDecorator.generateTransaction(listVolumeMappings), + getPhysicalConnections: getPhysicalConnections, + deleteNotRunningMicroservices: deleteNotRunningMicroservices, + updateRouteOverConnector: updateRouteOverConnector, + updatePortMappingOverConnector: updatePortMappingOverConnector }; diff --git a/src/services/registry-service.js b/src/services/registry-service.js index 5d3c44737..730cba299 100644 --- a/src/services/registry-service.js +++ b/src/services/registry-service.js @@ -43,21 +43,16 @@ const createRegistry = async function (registry, user, transaction) { registryCreate = AppHelper.deleteUndefinedFields(registryCreate); const createdRegistry = await RegistryManager.create(registryCreate, transaction); - await updateChangeTracking(user, transaction); + + await _updateChangeTracking(user, transaction); + return { id: createdRegistry.id } }; -const updateChangeTracking = async function (user, transaction) { - let fogs = await FogManager.findAll({userId: user.id}, transaction); - for (fog of fogs) { - await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.registries, transaction); - } -}; - -const findRegistries = async function (user, isCli, transaction) { - const queryRegistry = isCli +const findRegistries = async function (user, isCLI, transaction) { + const queryRegistry = isCLI ? {} : { [Op.or]: @@ -77,20 +72,20 @@ const findRegistries = async function (user, isCli, transaction) { } }; -const deleteRegistry = async function (registryData, user, isCli, transaction) { - await Validator.validate(registryData, Validator.schemas.registryDelete) - const queryData = isCli +const deleteRegistry = async function (registryData, user, isCLI, transaction) { + await Validator.validate(registryData, Validator.schemas.registryDelete); + const queryData = isCLI ? {id: registryData.id} : {id: registryData.id, userId: user.id}; const registry = await RegistryManager.findOne(queryData, transaction); if (!registry) { throw new Errors.NotFoundError(AppHelper.formatMessage(ErrorMessages.INVALID_REGISTRY_ID, registryData.id)); } - if (isCli) { + if (isCLI) { user = {id: registry.userId}; } await RegistryManager.delete(queryData, transaction); - await updateChangeTracking(user, transaction); + await _updateChangeTracking(user, transaction); }; const updateRegistry = async function (registry, registryId, user, isCLI, transaction) { @@ -131,12 +126,20 @@ const updateRegistry = async function (registry, registryId, user, isCLI, transa await RegistryManager.update(where, registryUpdate, transaction); - await updateChangeTracking(user, transaction); + await _updateChangeTracking(user, transaction); +}; + + +const _updateChangeTracking = async function (user, transaction) { + let fogs = await FogManager.findAll({userId: user.id}, transaction); + for (fog of fogs) { + await ChangeTrackingService.update(fog.uuid, ChangeTrackingService.events.registries, transaction); + } }; module.exports = { - findRegistries: TransactionDecorator.generateTransaction(findRegistries), createRegistry: TransactionDecorator.generateTransaction(createRegistry), + findRegistries: TransactionDecorator.generateTransaction(findRegistries), deleteRegistry: TransactionDecorator.generateTransaction(deleteRegistry), updateRegistry: TransactionDecorator.generateTransaction(updateRegistry) }; \ No newline at end of file diff --git a/src/services/user-service.js b/src/services/user-service.js index 53224dbeb..1d9a5456b 100644 --- a/src/services/user-service.js +++ b/src/services/user-service.js @@ -19,28 +19,19 @@ const Errors = require('../helpers/errors'); const ErrorMessages = require('../helpers/error-messages'); const Config = require('../config'); const ioFogManager = require('../sequelize/managers/iofog-manager'); - const emailActivationTemplate = require('../views/email-activation-temp'); const emailRecoveryTemplate = require('../views/email-temp'); const emailResetTemplate = require('../views/reset-password-temp'); const EmailActivationCodeService = require('./email-activation-code-service'); - const AccessTokenService = require('./access-token-service'); const TransactionDecorator = require('../decorators/transaction-decorator'); - const Validator = require('../schemas'); -const createUser = async function (user, transaction) { - return await UserManager.create(user, transaction) -}; - const signUp = async function (user, isCLI, transaction) { - let isEmailActivationEnabled = Config.get("Email:ActivationEnabled"); if (isEmailActivationEnabled) { - const newUser = await _handleCreateUser(user, isEmailActivationEnabled, transaction); const activationCodeData = await EmailActivationCodeService.generateActivationCode(transaction); @@ -56,7 +47,6 @@ const signUp = async function (user, isCLI, transaction) { }; const login = async function (credentials, isCLI, transaction) { - const user = await UserManager.findOne({ email: credentials.email }, transaction); @@ -138,7 +128,7 @@ const logout = async function (user, isCLI, transaction) { return await AccessTokenService.removeAccessTokenByUserId(user.id, transaction) }; -const updateDetails = async function (user, profileData, isCLI, transaction) { +const updateUserDetails = async function (user, profileData, isCLI, transaction) { if (isCLI) { await Validator.validate(profileData, Validator.schemas.updateUserProfileCLI); } else { @@ -147,7 +137,7 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { const password = (profileData.password) ? AppHelper.encryptText(profileData.password, user.email) : undefined; - const updateObject = isCLI ? + let updateObject = isCLI ? { firstName: profileData.firstName, lastName: profileData.lastName, @@ -159,7 +149,7 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { lastName: profileData.lastName }; - AppHelper.deleteUndefinedFields(updateObject); + updateObject = AppHelper.deleteUndefinedFields(updateObject); await UserManager.updateDetails(user, updateObject, transaction); @@ -171,7 +161,6 @@ const updateDetails = async function (user, profileData, isCLI, transaction) { }; const deleteUser = async function (force, user, isCLI, transaction) { - if (!force) { const ioFogArray = await ioFogManager.findAll({ userId: user.id @@ -321,7 +310,7 @@ async function _handleCreateUser(user, isEmailActivationEnabled, transaction) { async function _createNewUser(user, isEmailActivationEnabled, transaction) { user.emailActivated = !isEmailActivationEnabled; - return await createUser(user, transaction) + return await UserManager.create(user, transaction); } async function _notifyUserAboutActivationCode(email, url, emailSenderData, activationCodeData, transporter) { @@ -390,7 +379,7 @@ module.exports = { resendActivation: TransactionDecorator.generateTransaction(resendActivation), activateUser: TransactionDecorator.generateTransaction(activateUser), logout: TransactionDecorator.generateTransaction(logout), - updateUserDetails: TransactionDecorator.generateTransaction(updateDetails), + updateUserDetails: TransactionDecorator.generateTransaction(updateUserDetails), deleteUser: TransactionDecorator.generateTransaction(deleteUser), updateUserPassword: TransactionDecorator.generateTransaction(updateUserPassword), resetUserPassword: TransactionDecorator.generateTransaction(resetUserPassword), diff --git a/test/src/controllers/microservices-controller.test.js b/test/src/controllers/microservices-controller.test.js index 3baa7d889..897400c65 100644 --- a/test/src/controllers/microservices-controller.test.js +++ b/test/src/controllers/microservices-controller.test.js @@ -55,12 +55,12 @@ describe('Microservices Controller', () => { def('subject', () => $subject.createMicroserviceOnFogEndPoint($req, $user)); beforeEach(() => { - $sandbox.stub(MicroservicesService, 'createMicroserviceOnFog').returns($response); + $sandbox.stub(MicroservicesService, 'createMicroservice').returns($response); }); - it('calls MicroservicesService.createMicroserviceOnFog with correct args', async () => { + it('calls MicroservicesService.createMicroservice with correct args', async () => { await $subject; - expect(MicroservicesService.createMicroserviceOnFog).to.have.been.calledWith({ + expect(MicroservicesService.createMicroservice).to.have.been.calledWith({ name: $name, config: $config, catalogItemId: $catalogItemId, @@ -74,7 +74,7 @@ describe('Microservices Controller', () => { }, $user, false); }); - context('when MicroservicesService#createMicroserviceOnFog fails', () => { + context('when MicroservicesService#createMicroservice fails', () => { const error = 'Error!'; def('response', () => Promise.reject(error)); @@ -84,7 +84,7 @@ describe('Microservices Controller', () => { }) }); - context('when MicroservicesService#createMicroserviceOnFog succeeds', () => { + context('when MicroservicesService#createMicroservice succeeds', () => { it(`succeeds`, () => { return expect($subject).to.eventually.equal(undefined) }) diff --git a/test/src/services/connector-port-service.test.js b/test/src/services/connector-port-service.test.js new file mode 100644 index 000000000..c2cb72e22 --- /dev/null +++ b/test/src/services/connector-port-service.test.js @@ -0,0 +1,61 @@ +// TODO finish with qs.stringify mock EWC-452 +// describe('.openPortOnRandomConnector()', () => { +// const transaction = {}; +// const error = 'Error!'; +// +// const isPublicAccess = false; +// +// const connectors = [ +// { +// id: 15 +// }, +// { +// id: 16 +// } +// ]; +// +// def('subject', () => $subject.openPortOnRandomConnector(isPublicAccess, transaction)); +// def('findConnectorsResponse', () => Promise.resolve(connectors)); +// def('stringifyResponse', () => Promise.resolve()); +// +// beforeEach(() => { +// $sandbox.stub(ConnectorManager, 'findAll').returns($findConnectorsResponse); +// $sandbox.stub(qs, 'stringify').returns($stringifyResponse); +// }); +// +// it('calls ConnectorManager#findAll() with correct args', async () => { +// await $subject; +// expect(ConnectorManager.findAll).to.have.been.calledWith({}, transaction); +// }); +// +// context('when ConnectorManager#findAll() fails', () => { +// def('findConnectorsResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error) +// }) +// }); +// +// context('when ConnectorManager#findAll() succeeds', () => { +// it('calls qs#stringify() with correct args', async () => { +// await $subject; +// expect(qs.stringify).to.have.been.calledWith({ +// mapping: '{"type":"public","maxconnections":60,"heartbeatabsencethreshold":200000}' +// }); +// }); +// +// context('when qs#stringify() fails', () => { +// def('stringifyResponse', () => error); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.eventually.equal(undefined) +// }) +// }); +// +// context('when qs#stringify() succeeds', () => { +// it('fulfills the promise', () => { +// return expect($subject).to.eventually.equal(undefined) +// }) +// }) +// }) +// }); \ No newline at end of file diff --git a/test/src/services/connector-service.test.js b/test/src/services/connector-service.test.js new file mode 100644 index 000000000..0b98128e3 --- /dev/null +++ b/test/src/services/connector-service.test.js @@ -0,0 +1,458 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ConnectorManager = require('../../../src/sequelize/managers/connector-manager'); +const MicroserviceService = require('../../../src/services/microservices-service'); +const ConnectorService = require('../../../src/services/connector-service'); +const Validator = require('../../../src/schemas'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ConnectorPortManager = require('../../../src/sequelize/managers/connector-port-manager'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const qs = require('qs'); + +describe('Connector Service', () => { + def('subject', () => ConnectorService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.createConnector()', () => { + const transaction = {}; + const error = 'Error!'; + + const connectorData = { + name: 'testName', + domain: 'testDomain', + publicIp: 'testPublicIp', + cert: 'testCert', + isSelfSignedCert: false, + devMode: true + }; + + def('subject', () => $subject.createConnector(connectorData, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('isValidDomainResponse', () => false); + def('isValidPublicIpResponse', () => true); + def('isValidPublicIpResponse2', () => true); + def('findConnectorResponse', () => Promise.resolve()); + def('createConnectorResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'isValidDomain').returns($isValidDomainResponse); + $sandbox.stub(AppHelper, 'isValidPublicIP') + .onFirstCall().returns($isValidPublicIpResponse) + .onSecondCall().returns($isValidPublicIpResponse2); + $sandbox.stub(ConnectorManager, 'findOne').returns($findConnectorResponse); + $sandbox.stub(ConnectorManager, 'create').returns($createConnectorResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(connectorData, Validator.schemas.connectorCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#isValidDomain() with correct args', async () => { + await $subject; + expect(AppHelper.isValidDomain).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidDomain() fails', () => { + def('isValidDomainResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidDomain() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.publicIp); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls ConnectorManager#findOne() with correct args', async () => { + await $subject; + expect(ConnectorManager.findOne).to.have.been.calledWith({ + [Op.or]: [ + { + name: connectorData.name + }, + { + publicIp: connectorData.publicIp + }, + { + domain: connectorData.domain + } + ] + }, transaction); + }); + + context('when ConnectorManager#findOne() fails', () => { + def('findConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#findOne() succeeds', () => { + it('calls ConnectorManager#create() with correct args', async () => { + await $subject; + expect(ConnectorManager.create).to.have.been.calledWith(connectorData, transaction); + }); + + context('when ConnectorManager#create() fails', () => { + def('createConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.updateConnector()', () => { + const transaction = {}; + const error = 'Error!'; + + const connectorData = { + name: 'testName', + domain: 'testDomain', + publicIp: 'testPublicIp', + cert: 'testCert', + isSelfSignedCert: false, + devMode: true + }; + + const connector = {}; + + def('subject', () => $subject.updateConnector(connectorData, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('isValidDomainResponse', () => false); + def('isValidPublicIpResponse', () => true); + def('isValidPublicIpResponse2', () => true); + def('updateConnectorResponse', () => Promise.resolve()); + def('findOneConnectorResponse', () => Promise.resolve(connector)); + def('updateRouteOverConnectorResponse', () => Promise.resolve()); + def('updatePortMappingOverConnectorResponse', () => Promise.resolve()); + + const queryConnectorData = { + publicIp: connectorData.publicIp + }; + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'isValidDomain').returns($isValidDomainResponse); + $sandbox.stub(AppHelper, 'isValidPublicIP') + .onFirstCall().returns($isValidPublicIpResponse) + .onSecondCall().returns($isValidPublicIpResponse2); + $sandbox.stub(ConnectorManager, 'update').returns($updateConnectorResponse); + $sandbox.stub(ConnectorManager, 'findOne').returns($findOneConnectorResponse); + $sandbox.stub(MicroserviceService, 'updateRouteOverConnector').returns($updateRouteOverConnectorResponse); + $sandbox.stub(MicroserviceService, 'updatePortMappingOverConnector').returns($updatePortMappingOverConnectorResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(connectorData, Validator.schemas.connectorUpdate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#isValidDomain() with correct args', async () => { + await $subject; + expect(AppHelper.isValidDomain).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidDomain() fails', () => { + def('isValidDomainResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidDomain() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.domain); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls AppHelper#isValidPublicIP() with correct args', async () => { + await $subject; + expect(AppHelper.isValidPublicIP).to.have.been.calledWith(connectorData.publicIp); + }); + + context('when AppHelper#isValidPublicIP() fails', () => { + def('isValidPublicIpResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when AppHelper#isValidPublicIP() succeeds', () => { + it('calls ConnectorManager#update() with correct args', async () => { + await $subject; + expect(ConnectorManager.update).to.have.been.calledWith(queryConnectorData, connectorData, transaction); + }); + + context('when ConnectorManager#update() fails', () => { + def('updateConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#update() succeeds', () => { + it('calls ConnectorManager#findOne() with correct args', async () => { + await $subject; + expect(ConnectorManager.findOne).to.have.been.calledWith({ + publicIp: connectorData.publicIp + }, transaction); + }); + + context('when ConnectorManager#findOne() fails', () => { + def('findOneConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#findOne() succeeds', () => { + it('calls MicroserviceService#updateRouteOverConnector() with correct args', async () => { + await $subject; + expect(MicroserviceService.updateRouteOverConnector).to.have.been.calledWith(connector, transaction); + }); + + context('when MicroserviceService#updateRouteOverConnector() fails', () => { + def('updateRouteOverConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceService#updateRouteOverConnector() succeeds', () => { + it('calls MicroserviceService#updatePortMappingOverConnector() with correct args', async () => { + await $subject; + expect(MicroserviceService.updatePortMappingOverConnector).to.have.been.calledWith(connector, transaction); + }); + + context('when MicroserviceService#updatePortMappingOverConnector() fails', () => { + def('updatePortMappingOverConnectorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceService#updatePortMappingOverConnector() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.deleteConnector()', () => { + const transaction = {}; + const error = 'Error!'; + + const connectorData = { + name: 'testName', + domain: 'testDomain', + publicIp: 'testPublicIp', + cert: 'testCert', + isSelfSignedCert: false, + devMode: true + }; + + const connector = { + id: 15 + }; + + def('subject', () => $subject.deleteConnector(connectorData, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findConnectorResponse', () => Promise.resolve(connector)); + def('findConnectorPortsResponse', () => Promise.resolve()); + def('deleteConnectorResponse', () => Promise.resolve()); + + const queryConnectorData = { + publicIp: connectorData.publicIp + }; + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ConnectorManager, 'findOne').returns($findConnectorResponse); + $sandbox.stub(ConnectorPortManager, 'findAll').returns($findConnectorPortsResponse); + $sandbox.stub(ConnectorManager, 'delete').returns($deleteConnectorResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(connectorData, Validator.schemas.connectorDelete); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ConnectorManager#findOne() with correct args', async () => { + await $subject; + expect(ConnectorManager.findOne).to.have.been.calledWith(queryConnectorData, transaction); + }); + + context('when ConnectorManager#findOne() fails', () => { + def('findConnectorResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when ConnectorManager#findOne() succeeds', () => { + it('calls ConnectorPortManager#findAll() with correct args', async () => { + await $subject; + expect(ConnectorPortManager.findAll).to.have.been.calledWith({ + connectorId: connector.id + }, transaction); + }); + + context('when ConnectorPortManager#findAll() fails', () => { + def('findConnectorPortsResponse', () => { + }); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when ConnectorPortManager#findAll() succeeds', () => { + it('calls ConnectorManager#delete() with correct args', async () => { + await $subject; + expect(ConnectorManager.delete).to.have.been.calledWith(queryConnectorData, transaction); + }); + + context('when ConnectorManager#delete() fails', () => { + def('deleteConnectorResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined) + }) + }); + + context('when ConnectorManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }); + + describe('.getConnectorList()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getConnectorList(transaction)); + def('findConnectorsResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(ConnectorManager, 'findAll').returns($findConnectorsResponse); + }); + + it('calls ConnectorManager#findAll() with correct args', async () => { + await $subject; + expect(ConnectorManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when ConnectorManager#findAll() fails', () => { + def('findConnectorsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ConnectorManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }); +}); \ No newline at end of file diff --git a/test/src/services/controller-service.test.js b/test/src/services/controller-service.test.js new file mode 100644 index 000000000..98650722c --- /dev/null +++ b/test/src/services/controller-service.test.js @@ -0,0 +1,101 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ControllerService = require('../../../src/services/controller-service'); +const ioFogTypesManager = require('../../../src/sequelize/managers/iofog-type-manager'); +const Config = require('../../../src/config'); + +describe('Controller Service', () => { + def('subject', () => ControllerService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + const isCLI = false; + + describe('.getFogTypes()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getFogTypes(isCLI, transaction)); + def('findResponse', () => Promise.resolve([{ + id: 15, + name: 'testName', + image: 'testImage', + description: 'testDescription' + }])); + + beforeEach(() => { + $sandbox.stub(ioFogTypesManager, 'findAll').returns($findResponse); + }); + + it('calls ioFogTypesManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogTypesManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when ioFogTypesManager#findAll() fails', () => { + def('findResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ioFogTypesManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('fogTypes') + }) + }) + }); + + describe('.emailActivation()', () => { + const error = 'Error!'; + + def('subject', () => $subject.emailActivation(isCLI)); + def('getResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Config, 'get').returns($getResponse); + }); + + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith('Email:ActivationEnabled'); + }); + + context('when Config#get() fails', () => { + def('getResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Config#get() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('isEmailActivationEnabled') + }) + }) + }); + + + describe('.statusController()', () => { + const error = 'Error!'; + + def('subject', () => $subject.statusController(isCLI)); + def('getResponse', () => Promise.resolve()); + + beforeEach(() => { + // TODO daemon.status + //$sandbox.stub(daemon, 'status').returns($getResponse); + }); + + // it('Returns valid status', () => { + // return expect($subject).to.have.property('status') && + // expect($subject).to.have.property('timestamp') + // }) + }); + + +}); \ No newline at end of file diff --git a/test/src/services/diagnostic-service.test.js b/test/src/services/diagnostic-service.test.js new file mode 100644 index 000000000..d18b303cf --- /dev/null +++ b/test/src/services/diagnostic-service.test.js @@ -0,0 +1,502 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const StraceDiagnosticManager = require('../../../src/sequelize/managers/strace-diagnostics-manager'); +const DiagnosticService = require('../../../src/services/diagnostic-service'); +const MicroserviceService = require('../../../src/services/microservices-service'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const Validator = require('../../../src/schemas'); +const MicroserviceManager = require('../../../src/sequelize/managers/microservice-manager'); +const Config = require('../../../src/config'); + +describe('DiagnosticService Service', () => { + def('subject', () => DiagnosticService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = true; + + afterEach(() => $sandbox.restore()); + + describe('.changeMicroserviceStraceState()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = 'user!'; + + const uuid = 'testUuid'; + + const data = { + enable: true + }; + + const straceObj = { + straceRun: data.enable, + microserviceUuid: uuid + }; + + const microservice = { + iofogUuid: 'testIoFogUuid' + }; + + def('subject', () => $subject.changeMicroserviceStraceState(uuid, data, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('getMicroserviceResponse', () => Promise.resolve(microservice)); + def('updateOrCreateDiagnosticResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(MicroserviceService, 'getMicroservice').returns($getMicroserviceResponse); + $sandbox.stub(StraceDiagnosticManager, 'updateOrCreate').returns($updateOrCreateDiagnosticResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.straceStateUpdate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls MicroserviceService#getMicroservice() with correct args', async () => { + await $subject; + expect(MicroserviceService.getMicroservice).to.have.been.calledWith(uuid, user, isCLI, transaction); + }); + + context('when MicroserviceService#getMicroservice() fails', () => { + def('getMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceService#getMicroservice() succeeds', () => { + it('calls StraceDiagnosticManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(StraceDiagnosticManager.updateOrCreate).to.have.been.calledWith({ + microserviceUuid: uuid + }, straceObj, transaction); + }); + + context('when StraceDiagnosticManager#updateOrCreate() fails', () => { + def('updateOrCreateDiagnosticResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when StraceDiagnosticManager#updateOrCreate() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microservice.iofogUuid, + ChangeTrackingService.events.diagnostics, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }); + + describe('.getMicroserviceStraceData()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const data = { + format: 'string' + }; + + const microservice = { + iofogUuid: 'testIoFogUuid' + }; + + def('subject', () => $subject.getMicroserviceStraceData(uuid, data, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('getMicroserviceResponse', () => Promise.resolve(microservice)); + def('findStraceResponse', () => Promise.resolve({})); + def('configGetResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(MicroserviceManager, 'findOne').returns($getMicroserviceResponse); + $sandbox.stub(StraceDiagnosticManager, 'findOne').returns($findStraceResponse); + $sandbox.stub(Config, 'get').returns($configGetResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.straceGetData); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls MicroserviceManager#findOne() with correct args', async () => { + await $subject; + const microserviceWhere = isCLI + ? {uuid: uuid} + : {uuid: uuid, userId: user.id}; + expect(MicroserviceManager.findOne).to.have.been.calledWith(microserviceWhere, transaction); + }); + + + context('when MicroserviceManager#findOne() fails', () => { + def('getMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findOne() succeeds', () => { + it('calls StraceDiagnosticManager#findOne() with correct args', async () => { + await $subject; + expect(StraceDiagnosticManager.findOne).to.have.been.calledWith({ + microserviceUuid: uuid + }, transaction); + }); + + context('when StraceDiagnosticManager#findOne() fails', () => { + def('findStraceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when StraceDiagnosticManager#findOne() succeeds', () => { + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith('Diagnostics:DiagnosticDir'); + }); + + context('when Config#get() fails', () => { + def('configGetResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.eventually.have.property('data') + }) + }); + + context('when Config#get() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('data') + }) + }) + }) + }) + }) + }); + + // TODO handle FTP stuff + // describe('.postMicroserviceStraceDatatoFtp()', () => { + // const transaction = {}; + // const error = 'Error!'; + // + // const user = { + // id: 15 + // }; + // + // const uuid = 'testUuid'; + // + // const data = { + // ftpHost: 'testHost', + // ftpPort: 5555, + // ftpUser: 'testUser', + // ftpPass: 'testPass', + // ftpDestDir: 'testDir' + // }; + // + // const microservice = { + // iofogUuid: 'testIoFogUuid' + // }; + // + // def('subject', () => $subject.postMicroserviceStraceDatatoFtp(uuid, data, user, isCLI, transaction)); + // def('validatorResponse', () => Promise.resolve(true)); + // def('getMicroserviceResponse', () => Promise.resolve(microservice)); + // def('findStraceResponse', () => Promise.resolve({})); + // def('configGetResponse', () => Promise.resolve()); + // + // + // beforeEach(() => { + // $sandbox.stub(Validator, 'validate').returns($validatorResponse); + // $sandbox.stub(MicroserviceManager, 'findOne').returns($getMicroserviceResponse); + // $sandbox.stub(StraceDiagnosticManager, 'findOne').returns($findStraceResponse); + // $sandbox.stub(Config, 'get').returns($configGetResponse); + // }); + // + // it('calls Validator#validate() with correct args', async () => { + // await $subject; + // expect(Validator.validate).to.have.been.calledWith(data, Validator.schemas.stracePostToFtp); + // }); + // + // context('when Validator#validate() fails', () => { + // def('validatorResponse', () => Promise.reject(error)); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.rejectedWith(error) + // }) + // }); + // + // context('when Validator#validate() succeeds', () => { + // it('calls MicroserviceManager#findOne() with correct args', async () => { + // await $subject; + // const microserviceWhere = isCLI + // ? {uuid: uuid} + // : {uuid: uuid, userId: user.id}; + // expect(MicroserviceManager.findOne).to.have.been.calledWith(microserviceWhere, transaction); + // }); + // + // + // context('when MicroserviceManager#findOne() fails', () => { + // def('getMicroserviceResponse', () => Promise.reject(error)); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.rejectedWith(error) + // }) + // }); + // + // context('when MicroserviceManager#findOne() succeeds', () => { + // it('calls StraceDiagnosticManager#findOne() with correct args', async () => { + // await $subject; + // expect(StraceDiagnosticManager.findOne).to.have.been.calledWith({ + // microserviceUuid: uuid + // }, transaction); + // }); + // + // context('when StraceDiagnosticManager#findOne() fails', () => { + // def('findStraceResponse', () => Promise.reject(error)); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.rejectedWith(error) + // }) + // }); + // + // context('when StraceDiagnosticManager#findOne() succeeds', () => { + // it('calls Config#get() with correct args', async () => { + // await $subject; + // expect(Config.get).to.have.been.calledWith('Diagnostics:DiagnosticDir'); + // }); + // + // context('when Config#get() fails', () => { + // def('configGetResponse', () => error); + // + // it(`fails with ${error}`, () => { + // return expect($subject).to.be.eventually.have.property('data') + // }) + // }); + // + // context('when Config#get() succeeds', () => { + // it('fulfills the promise', () => { + // return expect($subject).to.eventually.have.property('data') + // }) + // }) + // }) + // }) + // }) + // }); + + describe('.postMicroserviceImageSnapshotCreate()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = 'user!'; + + const microserviceUuid = 'testUuid'; + + const microserviceToUpdate = { + imageSnapshot: 'get_image' + }; + + const microservice = { + iofogUuid: 'testIoFogUuid', + uuid: 'testMicroserviceUuid' + }; + + + def('subject', () => $subject.postMicroserviceImageSnapshotCreate(microserviceUuid, user, isCLI, transaction)); + def('findMicroserviceResponse', () => Promise.resolve(microservice)); + def('updateMicroserviceResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOneWithDependencies').returns($findMicroserviceResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls MicroserviceManager#findOneWithDependencies() with correct args', async () => { + await $subject; + const where = isCLI ? + { + uuid: microserviceUuid + } + : + { + uuid: microserviceUuid, + userId: user.id + }; + expect(MicroserviceManager.findOneWithDependencies).to.have.been.calledWith(where, {}, transaction); + }); + + context('when MicroserviceManager#findOneWithDependencies() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findOneWithDependencies() succeeds', () => { + it('calls MicroserviceManager#update() with correct args', async () => { + await $subject; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microservice.uuid + }, microserviceToUpdate, transaction); + }); + + context('when MicroserviceManager#update() fails', () => { + def('updateMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microservice.iofogUuid, + ChangeTrackingService.events.imageSnapshot, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }); + + describe('.getMicroserviceImageSnapshot()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = 'user!'; + + const microserviceUuid = 'testUuid'; + + const microserviceToUpdate = { + imageSnapshot: '' + }; + + const microservice = { + iofogUuid: 'testIoFogUuid', + uuid: 'testMicroserviceUuid', + imageSnapshot: 'testImagePath' + }; + + + def('subject', () => $subject.getMicroserviceImageSnapshot(microserviceUuid, user, isCLI, transaction)); + def('findMicroserviceResponse', () => Promise.resolve(microservice)); + def('updateMicroserviceResponse', () => Promise.resolve()); + + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOneWithDependencies').returns($findMicroserviceResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); + }); + + it('calls MicroserviceManager#findOneWithDependencies() with correct args', async () => { + await $subject; + const where = isCLI ? + { + uuid: microserviceUuid + } + : + { + uuid: microserviceUuid, + userId: user.id + }; + expect(MicroserviceManager.findOneWithDependencies).to.have.been.calledWith(where, {}, transaction); + }); + + context('when MicroserviceManager#findOneWithDependencies() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#findOneWithDependencies() succeeds', () => { + it('calls MicroserviceManager#update() with correct args', async () => { + await $subject; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microservice.uuid + }, microserviceToUpdate, transaction); + }); + + context('when MicroserviceManager#update() fails', () => { + def('updateMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when MicroserviceManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(microservice.imageSnapshot) + }) + }) + }) + }); + + +}); \ No newline at end of file diff --git a/test/src/services/email-activation-code-service.test.js b/test/src/services/email-activation-code-service.test.js new file mode 100644 index 000000000..862007ae5 --- /dev/null +++ b/test/src/services/email-activation-code-service.test.js @@ -0,0 +1,179 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const EmailActivationCodeManager = require('../../../src/sequelize/managers/email-activation-code-manager'); +const EmailActivationCodeService = require('../../../src/services/email-activation-code-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ErrorMessages = require('../../../src/helpers/error-messages'); + + +describe('EmailActivationCode Service', () => { + def('subject', () => EmailActivationCodeService); + def('sandbox', () => sinon.createSandbox()); + + afterEach(() => $sandbox.restore()); + + describe('.generateActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const response = { + activationCode: "abcdefgwdwdwdwdwd", + expirationTime: new Date().getTime() + ((60 * 60 * 24 * 3) * 1000) + }; + + def('subject', () => $subject.generateActivationCode(transaction)); + def('generateStringResponse', () => response.activationCode); + def('findActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateStringResponse); + $sandbox.stub(EmailActivationCodeManager, 'getByActivationCode').returns($findActivationCodeResponse); + }); + + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + expect(AppHelper.generateRandomString).to.have.been.calledWith(16); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('activationCode') + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls EmailActivationCodeManager#getByActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.getByActivationCode).to.have.been.calledWith(response.activationCode, + transaction); + }); + + context('when EmailActivationCodeManager#getByActivationCode() fails', () => { + def('findActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when EmailActivationCodeManager#getByActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('activationCode') && + expect($subject).to.eventually.have.property('expirationTime') + }) + }) + }) + }); + + describe('.saveActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const userId = 15; + + const activationCodeData = { + activationCode: "abcdefgwdwdwdwdwd", + expirationTime: new Date().getTime() + ((60 * 60 * 24 * 3) * 1000) + }; + + def('subject', () => $subject.saveActivationCode(userId, activationCodeData, transaction)); + def('createActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(EmailActivationCodeManager, 'createActivationCode').returns($createActivationCodeResponse); + }); + + it('calls EmailActivationCodeManager#createActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.createActivationCode).to.have.been.calledWith(userId, + activationCodeData.activationCode, activationCodeData.expirationTime, transaction); + }); + + context('when EmailActivationCodeManager#createActivationCode() fails', () => { + def('createActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(ErrorMessages.UNABLE_TO_CREATE_ACTIVATION_CODE); + }) + }); + + context('when EmailActivationCodeManager#createActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(undefined) + }) + }) + }); + + describe('.verifyActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const activationCode = "abcdefgwdwdwdwdwd"; + + def('subject', () => $subject.verifyActivationCode(activationCode, transaction)); + def('verifyActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(EmailActivationCodeManager, 'verifyActivationCode').returns($verifyActivationCodeResponse); + }); + + it('calls EmailActivationCodeManager#verifyActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.verifyActivationCode).to.have.been.calledWith(activationCode, transaction); + }); + + context('when EmailActivationCodeManager#verifyActivationCode() fails', () => { + def('verifyActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(ErrorMessages.UNABLE_TO_GET_ACTIVATION_CODE); + }) + }); + + context('when EmailActivationCodeManager#verifyActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(undefined) + }) + }) + }); + + describe('.deleteActivationCode()', () => { + const transaction = {}; + const error = 'Error!'; + + const activationCode = "abcdefgwdwdwdwdwd"; + + def('subject', () => $subject.deleteActivationCode(activationCode, transaction)); + def('deleteActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(EmailActivationCodeManager, 'delete').returns($deleteActivationCodeResponse); + }); + + it('calls EmailActivationCodeManager#delete() with correct args', async () => { + await $subject; + expect(EmailActivationCodeManager.delete).to.have.been.calledWith({ + activationCode: activationCode + }, transaction); + }); + + context('when EmailActivationCodeManager#delete() fails', () => { + def('deleteActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when EmailActivationCodeManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(undefined) + }) + }) + }); + + +}); \ No newline at end of file diff --git a/test/src/services/flow-service.test.js b/test/src/services/flow-service.test.js new file mode 100644 index 000000000..d0045af4b --- /dev/null +++ b/test/src/services/flow-service.test.js @@ -0,0 +1,531 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const FlowManager = require('../../../src/sequelize/managers/flow-manager'); +const FlowService = require('../../../src/services/flow-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const Validator = require('../../../src/schemas'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const ErrorMessages = require('../../../src/helpers/error-messages'); + +describe('Flow Service', () => { + def('subject', () => FlowService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.createFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = null; + + const flowData = { + name: 'testName', + description: 'testDescription', + isActivated: false + }; + + const flowToCreate = { + name: flowData.name, + description: flowData.description, + isActivated: flowData.isActivated, + userId: user.id + }; + + const response = { + id: 25 + }; + + def('subject', () => $subject.createFlow(flowData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findFlowResponse', () => Promise.resolve()); + def('deleteUndefinedFieldsResponse', () => flowToCreate); + def('createFlowResponse', () => Promise.resolve(response)); + + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(FlowManager, 'findOne').returns($findFlowResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(FlowManager, 'create').returns($createFlowResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(flowData, Validator.schemas.flowCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls FlowManager#findOne() with correct args', async () => { + await $subject; + const where = flowId + ? {name: flowData.name, id: {[Op.ne]: flowId, userId: user.id}} + : {name: flowData.name, userId: user.id}; + + expect(FlowManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOne() fails', () => { + def('findFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when FlowManager#findOne() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(flowToCreate); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls FlowManager#create() with correct args', async () => { + await $subject; + + expect(FlowManager.create).to.have.been.calledWith(flowToCreate); + }); + + context('when FlowManager#create() fails', () => { + def('createFlowResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id') + }) + }); + + context('when FlowManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('id') + }) + }) + }) + }) + }) + }); + + describe('.deleteFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = 75; + + const whereObj = { + id: flowId, + userId: user.id + }; + + const flowWithMicroservices = { + microservices: [ + { + iofogUuid: 15 + } + ] + }; + + def('subject', () => $subject.deleteFlow(flowId, user, isCLI, transaction)); + def('deleteUndefinedFieldsResponse', () => whereObj); + def('findFlowMicroservicesResponse', () => Promise.resolve(flowWithMicroservices)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + def('deleteFlowResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(FlowManager, 'findFlowMicroservices').returns($findFlowMicroservicesResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + $sandbox.stub(FlowManager, 'delete').returns($deleteFlowResponse); + }); + + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(whereObj); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls FlowManager#findFlowMicroservices() with correct args', async () => { + await $subject; + + expect(FlowManager.findFlowMicroservices).to.have.been.calledWith({ + id: flowId + }, transaction); + }); + + context('when FlowManager#findFlowMicroservices() fails', () => { + def('findFlowMicroservicesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findFlowMicroservices() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(flowWithMicroservices.microservices[0].iofogUuid, + ChangeTrackingService.events.microserviceFull, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('calls FlowManager#delete() with correct args', async () => { + await $subject; + + expect(FlowManager.delete).to.have.been.calledWith(whereObj, transaction); + }); + + context('when FlowManager#delete() fails', () => { + def('deleteFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }); + + + describe('.updateFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = 75; + + const oldFlowData = { + name: 'testName', + description: 'testDescription', + isActivated: true + }; + + const flowData = { + name: 'testName', + description: 'testDescription', + isActivated: false + }; + + const flowWithMicroservices = { + microservices: [ + { + iofogUuid: 15 + } + ] + }; + + def('subject', () => $subject.updateFlow(flowData, flowId, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findExcludedFlowResponse', () => Promise.resolve(oldFlowData)); + def('findFlowResponse', () => Promise.resolve()); + def('deleteUndefinedFieldsResponse', () => flowData); + def('updateFlowResponse', () => Promise.resolve()); + def('findFlowMicroservicesResponse', () => Promise.resolve(flowWithMicroservices)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(FlowManager, 'findOneExcludeFields').returns($findExcludedFlowResponse); + $sandbox.stub(FlowManager, 'findOne').returns($findFlowResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(FlowManager, 'update').returns($updateFlowResponse); + $sandbox.stub(FlowManager, 'findFlowMicroservices').returns($findFlowMicroservicesResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(flowData, Validator.schemas.flowUpdate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls FlowManager#findOneExcludeFields() with correct args', async () => { + await $subject; + + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + expect(FlowManager.findOneExcludeFields).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOneExcludeFields() fails', () => { + def('findExcludedFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findOneExcludeFields() succeeds', () => { + it('calls FlowManager#findOne() with correct args', async () => { + await $subject; + + const where = flowId + ? {name: flowData.name, id: {[Op.ne]: flowId, userId: user.id}} + : {name: flowData.name, userId: user.id}; + expect(FlowManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOne() fails', () => { + def('findFlowResponse', () => Promise.reject(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, + flowData.name))); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(AppHelper.formatMessage(ErrorMessages.DUPLICATE_NAME, + flowData.name)); + }) + }); + + context('when FlowManager#findOne() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(flowData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls FlowManager#update() with correct args', async () => { + await $subject; + + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + expect(FlowManager.update).to.have.been.calledWith(where, flowData, transaction); + }); + + context('when FlowManager#update() fails', () => { + def('updateFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#update() succeeds', () => { + it('calls FlowManager#findFlowMicroservices() with correct args', async () => { + await $subject; + + expect(FlowManager.findFlowMicroservices).to.have.been.calledWith({ + id: flowId + }, transaction); + }); + + context('when FlowManager#findFlowMicroservices() fails', () => { + def('findFlowMicroservicesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findFlowMicroservices() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(flowWithMicroservices.microservices[0].iofogUuid, + ChangeTrackingService.events.microserviceFull, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.getUserFlows()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flow = { + userId: user.id + }; + + def('subject', () => $subject.getUserFlows(user, isCLI, transaction)); + def('findExcludedFlowResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(FlowManager, 'findAllExcludeFields').returns($findExcludedFlowResponse); + }); + + it('calls FlowManager#findAllExcludeFields() with correct args', async () => { + await $subject; + expect(FlowManager.findAllExcludeFields).to.have.been.calledWith(flow, transaction); + }); + + context('when FlowManager#findAllExcludeFields() fails', () => { + def('findExcludedFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findAllExcludeFields() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('flows'); + }) + }) + }); + + + describe('.getAllFlows()', () => { + const transaction = {}; + const error = 'Error!'; + + def('subject', () => $subject.getAllFlows(isCLI, transaction)); + def('findAllFlowsResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(FlowManager, 'findAll').returns($findAllFlowsResponse); + }); + + it('calls FlowManager#findAll() with correct args', async () => { + await $subject; + expect(FlowManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when FlowManager#findAll() fails', () => { + def('findAllFlowsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('flows'); + }) + }) + }); + + describe('.getFlow()', () => { + const transaction = {}; + const error = 'Error!'; + + const flowId = 75; + + const user = { + id: 15 + }; + + def('subject', () => $subject.getFlow(flowId, user, isCLI, transaction)); + def('findFlowResponse', () => Promise.resolve({})); + + beforeEach(() => { + $sandbox.stub(FlowManager, 'findOneExcludeFields').returns($findFlowResponse); + }); + + it('calls FlowManager#findOneExcludeFields() with correct args', async () => { + await $subject; + const where = isCLI + ? {id: flowId} + : {id: flowId, userId: user.id}; + expect(FlowManager.findOneExcludeFields).to.have.been.calledWith(where, transaction); + }); + + context('when FlowManager#findOneExcludeFields() fails', () => { + def('findFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowManager#findOneExcludeFields() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal({}); + }) + }) + }); + +}); \ No newline at end of file diff --git a/test/src/services/iofog-service.test.js b/test/src/services/iofog-service.test.js new file mode 100644 index 000000000..485053a26 --- /dev/null +++ b/test/src/services/iofog-service.test.js @@ -0,0 +1,1708 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const ioFogManager = require('../../../src/sequelize/managers/iofog-manager'); +const ioFogService = require('../../../src/services/iofog-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const Validator = require('../../../src/schemas'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const CatalogService = require('../../../src/services/catalog-service'); +const MicroserviceManager = require('../../../src/sequelize/managers/microservice-manager'); +const ioFogProvisionKeyManager = require('../../../src/sequelize/managers/iofog-provision-key-manager'); +const ioFogVersionCommandManager = require('../../../src/sequelize/managers/iofog-version-command-manager'); +const HWInfoManager = require('../../../src/sequelize/managers/hw-info-manager'); +const USBInfoManager = require('../../../src/sequelize/managers/usb-info-manager'); + +describe('ioFog Service', () => { + def('subject', () => ioFogService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.createFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + const uuid2 = 'testUuid2'; + const uuid3 = 'testUuid3'; + + const fogData = { + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + deviceScanFrequency: 26, + bluetoothEnabled: true, + watchdogEnabled: false, + abstractedHardwareEnabled: true, + fogType: 1 + }; + + const createFogData = { + uuid: uuid, + name: fogData.name, + location: fogData.location, + latitude: fogData.latitude, + longitude: fogData.longitude, + gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, + description: fogData.description, + dockerUrl: fogData.dockerUrl, + diskLimit: fogData.diskLimit, + diskDirectory: fogData.diskDirectory, + memoryLimit: fogData.memoryLimit, + cpuLimit: fogData.cpuLimit, + logLimit: fogData.logLimit, + logDirectory: fogData.logDirectory, + logFileCount: fogData.logFileCount, + statusFrequency: fogData.statusFrequency, + changeFrequency: fogData.changeFrequency, + deviceScanFrequency: fogData.deviceScanFrequency, + bluetoothEnabled: fogData.bluetoothEnabled, + watchdogEnabled: fogData.watchdogEnabled, + abstractedHardwareEnabled: fogData.abstractedHardwareEnabled, + fogTypeId: fogData.fogType, + userId: user.id + }; + + const halItem = { + id: 10 + }; + + const oldFog = null; + const halMicroserviceData = { + uuid: uuid2, + name: `Hal for Fog ${createFogData.uuid}`, + config: '{}', + catalogItemId: halItem.id, + iofogUuid: createFogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + + const bluetoothItem = { + id: 10 + }; + const bluetoothMicroserviceData = { + uuid: uuid3, + name: `Bluetooth for Fog ${createFogData.uuid}`, + config: '{}', + catalogItemId: bluetoothItem.id, + iofogUuid: createFogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + + const response = { + uuid: uuid + }; + + def('subject', () => $subject.createFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('generateRandomStringResponse', () => uuid); + def('generateRandomStringResponse2', () => uuid2); + def('generateRandomStringResponse3', () => uuid3); + def('deleteUndefinedFieldsResponse', () => createFogData); + def('createIoFogResponse', () => Promise.resolve(response)); + def('createChangeTrackingResponse', () => Promise.resolve()); + def('getHalCatalogItemResponse', () => Promise.resolve(halItem)); + def('createMicroserviceResponse', () => Promise.resolve()); + def('createMicroserviceResponse2', () => Promise.resolve()); + def('getBluetoothCatalogItemResponse', () => Promise.resolve(bluetoothItem)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'generateRandomString') + .onFirstCall().returns($generateRandomStringResponse) + .onSecondCall().returns($generateRandomStringResponse2) + .onThirdCall().returns($generateRandomStringResponse3); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(ioFogManager, 'create').returns($createIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'create').returns($createChangeTrackingResponse); + $sandbox.stub(CatalogService, 'getHalCatalogItem').returns($getHalCatalogItemResponse); + $sandbox.stub(MicroserviceManager, 'create') + .onFirstCall().returns($createMicroserviceResponse) + .onSecondCall().returns($createMicroserviceResponse2); + $sandbox.stub(CatalogService, 'getBluetoothCatalogItem').returns($getBluetoothCatalogItemResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(createFogData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid') + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls ioFogManager#create() with correct args', async () => { + await $subject; + + expect(ioFogManager.create).to.have.been.calledWith(createFogData); + }); + + context('when ioFogManager#create() fails', () => { + def('createIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#create() succeeds', () => { + it('calls ChangeTrackingService#create() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.create).to.have.been.calledWith(uuid, transaction); + }); + + context('when ChangeTrackingService#create() fails', () => { + def('createIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#create() succeeds', () => { + it('calls CatalogService#getHalCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getHalCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getHalCatalogItem() fails', () => { + def('getHalCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getHalCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(halMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls CatalogService#getBluetoothCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getBluetoothCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getBluetoothCatalogItem() fails', () => { + def('getBluetoothCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getBluetoothCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse3', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(bluetoothMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(createFogData.uuid, + ChangeTrackingService.events.microserviceCommon, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('uuid') + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.updateFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + const uuid2 = 'testUuid2'; + const uuid3 = 'testUuid3'; + + const fogData = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + deviceScanFrequency: 26, + bluetoothEnabled: true, + watchdogEnabled: false, + abstractedHardwareEnabled: true, + fogType: 1 + }; + + const oldFog = { + uuid: uuid2, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + let updateFogData = { + name: fogData.name, + location: fogData.location, + latitude: fogData.latitude, + longitude: fogData.longitude, + gpsMode: fogData.latitude || fogData.longitude ? 'manual' : undefined, + description: fogData.description, + dockerUrl: fogData.dockerUrl, + diskLimit: fogData.diskLimit, + diskDirectory: fogData.diskDirectory, + memoryLimit: fogData.memoryLimit, + cpuLimit: fogData.cpuLimit, + logLimit: fogData.logLimit, + logDirectory: fogData.logDirectory, + logFileCount: fogData.logFileCount, + statusFrequency: fogData.statusFrequency, + changeFrequency: fogData.changeFrequency, + deviceScanFrequency: fogData.deviceScanFrequency, + bluetoothEnabled: fogData.bluetoothEnabled, + watchdogEnabled: fogData.watchdogEnabled, + abstractedHardwareEnabled: fogData.abstractedHardwareEnabled, + fogTypeId: fogData.fogType, + }; + + const halItem = { + id: 10 + }; + const halMicroserviceData = { + uuid: uuid2, + name: `Hal for Fog ${fogData.uuid}`, + config: '{}', + catalogItemId: halItem.id, + iofogUuid: fogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + + const bluetoothItem = { + id: 10 + }; + const bluetoothMicroserviceData = { + uuid: uuid3, + name: `Bluetooth for Fog ${fogData.uuid}`, + config: '{}', + catalogItemId: bluetoothItem.id, + iofogUuid: fogData.uuid, + rootHostAccess: true, + logSize: 50, + userId: oldFog ? oldFog.userId : user.id, + configLastUpdated: date + }; + + def('subject', () => $subject.updateFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => updateFogData); + def('findIoFogResponse', () => Promise.resolve(oldFog)); + def('updateIoFogResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse2', () => Promise.resolve()); + def('getHalCatalogItemResponse', () => Promise.resolve(halItem)); + def('generateRandomStringResponse', () => uuid2); + def('generateRandomStringResponse2', () => uuid3); + def('createMicroserviceResponse', () => Promise.resolve()); + def('createMicroserviceResponse2', () => Promise.resolve()); + def('getBluetoothCatalogItemResponse', () => Promise.resolve(bluetoothItem)); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'update') + .onFirstCall().returns($updateChangeTrackingResponse) + .onSecondCall().returns($updateChangeTrackingResponse2); + $sandbox.stub(CatalogService, 'getHalCatalogItem').returns($getHalCatalogItemResponse); + $sandbox.stub(AppHelper, 'generateRandomString') + .onFirstCall().returns($generateRandomStringResponse) + .onSecondCall().returns($generateRandomStringResponse2); + $sandbox.stub(MicroserviceManager, 'create') + .onFirstCall().returns($createMicroserviceResponse) + .onSecondCall().returns($createMicroserviceResponse2); + $sandbox.stub(CatalogService, 'getBluetoothCatalogItem').returns($getBluetoothCatalogItemResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogUpdate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(updateFogData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + + expect(ioFogManager.update).to.have.been.calledWith(queryFogData, + updateFogData, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(uuid, + ChangeTrackingService.events.config, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('calls CatalogService#getHalCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getHalCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getHalCatalogItem() fails', () => { + def('getHalCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getHalCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(halMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls CatalogService#getBluetoothCatalogItem() with correct args', async () => { + await $subject; + + expect(CatalogService.getBluetoothCatalogItem).to.have.been.calledWith(transaction); + }); + + context('when CatalogService#getBluetoothCatalogItem() fails', () => { + def('getBluetoothCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getBluetoothCatalogItem() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + + expect(MicroserviceManager.create).to.have.been.calledWith(bluetoothMicroserviceData, transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('createMicroserviceResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(fogData.uuid, + ChangeTrackingService.events.microserviceCommon, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.deleteFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + + const fogData = { + uuid: uuid + }; + + const fog = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + daemonStatus: 'RUNNING', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + lastStatusTime: 1555, + ipAddress: 'testIpAddress', + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + const toUpdate = { + daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0' + }; + + def('subject', () => $subject.deleteFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve(fog)); + def('updateIoFogResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogDelete); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + const query = { + uuid: uuid + }; + expect(ioFogManager.update).to.have.been.calledWith(query, + toUpdate, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + + expect(ChangeTrackingService.update).to.have.been.calledWith(uuid, ChangeTrackingService.events.deleteNode, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }); + + describe('.getFog()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + + const fogData = { + uuid: uuid + }; + + const fog = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + daemonStatus: 'RUNNING', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + lastStatusTime: 1555, + ipAddress: 'testIpAddress', + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + const toUpdate = { + daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0' + }; + + const fogResponse = { + uuid: fog.uuid, + name: fog.name, + location: fog.location, + gpsMode: fog.gpsMode, + latitude: fog.latitude, + longitude: fog.longitude, + description: fog.description, + lastActive: fog.lastActive, + daemonStatus: 'UNKNOWN', + daemonOperatingDuration: fog.daemonOperatingDuration, + daemonLastStart: fog.daemonLastStart, + memoryUsage: fog.memoryUsage, + diskUsage: fog.diskUsage, + cpuUsage: fog.cpuUsage, + memoryViolation: fog.memoryViolation, + diskViolation: fog.diskViolation, + cpuViolation: fog.cpuViolation, + catalogItemStatus: fog.catalogItemStatus, + repositoryCount: fog.repositoryCount, + repositoryStatus: fog.repositoryStatus, + systemTime: fog.systemTime, + lastStatusTime: fog.lastStatusTime, + ipAddress: '0.0.0.0', + processedMessages: fog.processedMessages, + catalogItemMessageCounts: fog.catalogItemMessageCounts, + messageSpeed: fog.messageSpeed, + lastCommandTime: fog.lastCommandTime, + networkInterface: fog.networkInterface, + dockerUrl: fog.dockerUrl, + diskLimit: fog.diskLimit, + diskDirectory: fog.diskDirectory, + memoryLimit: fog.memoryLimit, + cpuLimit: fog.cpuLimit, + logLimit: fog.logLimit, + logDirectory: fog.logDirectory, + bluetoothEnabled: fog.bluetoothEnabled, + abstractedHardwareEnabled: fog.abstractedHardwareEnabled, + logFileCount: fog.logFileCount, + version: fog.version, + isReadyToUpgrade: fog.isReadyToUpgrade, + isReadyToRollback: fog.isReadyToRollback, + statusFrequency: fog.statusFrequency, + changeFrequency: fog.changeFrequency, + deviceScanFrequency: fog.deviceScanFrequency, + tunnel: fog.tunnel, + watchdogEnabled: fog.watchdogEnabled, + fogTypeId: fog.fogTypeId, + userId: fog.userId + }; + + def('subject', () => $subject.getFog(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve(fog)); + def('updateIoFogResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogGet); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + const query = { + uuid: uuid + }; + expect(ioFogManager.update).to.have.been.calledWith(query, + toUpdate, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(fogResponse); + }) + }) + }) + }) + }); + + describe('.getFogList()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const date = 155555555; + + const uuid = 'testUuid'; + + const fog = { + uuid: uuid, + name: 'testName', + location: 'testLocation', + latitude: 45, + longitude: 46, + description: 'testDescription', + dockerUrl: 'testDockerUrl', + diskLimit: 15, + diskDirectory: 'testDirectory', + daemonStatus: 'RUNNING', + memoryLimit: 55, + cpuLimit: 56, + logLimit: 57, + logDirectory: 'testLogDirectory', + logFileCount: 23, + statusFrequency: 24, + changeFrequency: 25, + lastStatusTime: 1555, + ipAddress: 'testIpAddress', + deviceScanFrequency: 26, + bluetoothEnabled: false, + watchdogEnabled: false, + abstractedHardwareEnabled: false, + fogType: 1 + }; + + const fogs = [fog]; + + const queryFogData = isCLI + ? {} + : {userId: user.id}; + + const toUpdate = { + daemonStatus: 'UNKNOWN', ipAddress: '0.0.0.0' + }; + + const filters = []; + + def('subject', () => $subject.getFogList(filters, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findAllIoFogResponse', () => Promise.resolve(fogs)); + def('updateIoFogResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findAll').returns($findAllIoFogResponse); + $sandbox.stub(ioFogManager, 'update').returns($updateIoFogResponse); + + $sandbox.stub(Date, 'now').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(filters, Validator.schemas.iofogFilters); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findAll() with correct args', async () => { + await $subject; + + expect(ioFogManager.findAll).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findAllIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls ioFogManager#update() with correct args', async () => { + await $subject; + const query = { + uuid: uuid + }; + expect(ioFogManager.update).to.have.been.calledWith(query, + toUpdate, transaction); + }); + + context('when ioFogManager#update() fails', () => { + def('updateIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('fogs'); + }) + }) + }) + }) + }); + + describe('.generateProvisioningKey()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const fogData = { + uuid: uuid + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + const provisionKey = 'tttttttt'; + const expirationTime = date + (20 * 60 * 1000); + + const newProvision = { + iofogUuid: fogData.uuid, + provisionKey: provisionKey, + expirationTime: expirationTime, + }; + + def('subject', () => $subject.generateProvisioningKey(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('generateRandomStringResponse', () => provisionKey); + def('findIoFogResponse', () => Promise.resolve({})); + def('updateOrCreateProvisionKeyResponse', () => Promise.resolve(newProvision)); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateRandomStringResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ioFogProvisionKeyManager, 'updateOrCreate').returns($updateOrCreateProvisionKeyResponse); + + $sandbox.stub(Date.prototype, 'getTime').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogGenerateProvision); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(8); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('key'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogProvisionKeyManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(ioFogProvisionKeyManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: fogData.uuid + }, newProvision, transaction); + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal({ + key: provisionKey, + expirationTime: expirationTime + }) + }) + }) + }) + }) + }) + }); + + describe('.setFogVersionCommand()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const fogVersionData = { + uuid: uuid, + versionCommand: 'upgrade' + }; + + const queryFogData = isCLI + ? {uuid: fogVersionData.uuid} + : {uuid: fogVersionData.uuid, userId: user.id}; + + const ioFog = { + uuid: uuid, + isReadyToUpgrade: true, + isReadyToRollback: true + }; + + const newVersionCommand = { + iofogUuid: fogVersionData.uuid, + versionCommand: fogVersionData.versionCommand + }; + + const provisionKey = 'tttttttt'; + const expirationTime = date + (20 * 60 * 1000); + + const newProvision = { + iofogUuid: uuid, + provisionKey: provisionKey, + expirationTime: expirationTime, + }; + + def('subject', () => $subject.setFogVersionCommand(fogVersionData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve(ioFog)); + def('generateRandomStringResponse', () => provisionKey); + def('updateOrCreateProvisionKeyResponse', () => Promise.resolve(newProvision)); + def('findIoFogVersionCommandResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateRandomStringResponse); + $sandbox.stub(ioFogProvisionKeyManager, 'updateOrCreate').returns($updateOrCreateProvisionKeyResponse); + $sandbox.stub(ioFogVersionCommandManager, 'updateOrCreate').returns($findIoFogVersionCommandResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + + $sandbox.stub(Date.prototype, 'getTime').returns($dateResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogVersionData, Validator.schemas.iofogSetVersionCommand); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith({ + uuid: fogVersionData.uuid + }, Validator.schemas.iofogGenerateProvision); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + + expect(AppHelper.generateRandomString).to.have.been.calledWith(8); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ioFogProvisionKeyManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(ioFogProvisionKeyManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: uuid + }, newProvision, transaction); + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogProvisionKeyManager#updateOrCreate() succeeds', () => { + it('calls ioFogVersionCommandManager#updateOrCreate() with correct args', async () => { + await $subject; + expect(ioFogVersionCommandManager.updateOrCreate).to.have.been.calledWith({ + iofogUuid: fogVersionData.uuid + }, newVersionCommand, transaction); + }); + + context('when ioFogVersionCommandManager#updateOrCreate() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogVersionCommandManager#updateOrCreate() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(fogVersionData.uuid, + ChangeTrackingService.events.version, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateOrCreateProvisionKeyResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.setFogRebootCommand()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const fogData = { + uuid: uuid + }; + + const queryFogData = isCLI + ? {uuid: fogData.uuid} + : {uuid: fogData.uuid, userId: user.id}; + + def('subject', () => $subject.setFogRebootCommand(fogData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve({})); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(fogData, Validator.schemas.iofogReboot); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith(queryFogData, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(fogData.uuid, + ChangeTrackingService.events.reboot, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }); + + describe('.getHalHardwareInfo()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const uuidObj = { + uuid: uuid + }; + + def('subject', () => $subject.getHalHardwareInfo(uuidObj, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve({})); + def('findHalHardwareResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(HWInfoManager, 'findOne').returns($findHalHardwareResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(uuidObj, Validator.schemas.halGet); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith({ + uuid: uuidObj.uuid + }, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls HWInfoManager#findOne() with correct args', async () => { + await $subject; + expect(HWInfoManager.findOne).to.have.been.calledWith({ + iofogUuid: uuidObj.uuid + }, transaction); + }); + + context('when HWInfoManager#findOne() fails', () => { + def('findHalHardwareResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when HWInfoManager#findOne() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }); + + describe('.getHalUsbInfo()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const uuid = 'testUuid'; + + const date = 155555555; + + const uuidObj = { + uuid: uuid + }; + + def('subject', () => $subject.getHalUsbInfo(uuidObj, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findIoFogResponse', () => Promise.resolve({})); + def('findHalUsbResponse', () => Promise.resolve()); + + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(ioFogManager, 'findOne').returns($findIoFogResponse); + $sandbox.stub(USBInfoManager, 'findOne').returns($findHalUsbResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(uuidObj, Validator.schemas.halGet); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls ioFogManager#findOne() with correct args', async () => { + await $subject; + expect(ioFogManager.findOne).to.have.been.calledWith({ + uuid: uuidObj.uuid + }, transaction); + }); + + context('when ioFogManager#findOne() fails', () => { + def('findIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findOne() succeeds', () => { + it('calls USBInfoManager#findOne() with correct args', async () => { + await $subject; + expect(USBInfoManager.findOne).to.have.been.calledWith({ + iofogUuid: uuidObj.uuid + }, transaction); + }); + + context('when USBInfoManager#findOne() fails', () => { + def('findHalUsbResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when USBInfoManager#findOne() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }); + + +}); \ No newline at end of file diff --git a/test/src/services/microservices-service.test.js b/test/src/services/microservices-service.test.js new file mode 100644 index 000000000..3b7941eae --- /dev/null +++ b/test/src/services/microservices-service.test.js @@ -0,0 +1,1334 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const MicroserviceManager = require('../../../src/sequelize/managers/microservice-manager'); +const MicroservicesService = require('../../../src/services/microservices-service'); +const AppHelper = require('../../../src/helpers/app-helper'); +const Validator = require('../../../src/schemas'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const CatalogService = require('../../../src/services/catalog-service'); +const FlowService = require('../../../src/services/flow-service'); +const ioFogService = require('../../../src/services/iofog-service'); +const MicroservicePortManager = require('../../../src/sequelize/managers/microservice-port-manager'); +const VolumeMappingManager = require('../../../src/sequelize/managers/volume-mapping-manager'); +const MicroserviceStatusManager = require('../../../src/sequelize/managers/microservice-status-manager'); +const RoutingManager = require('../../../src/sequelize/managers/routing-manager'); +const Op = require('sequelize').Op; + +describe('Microservices Service', () => { + def('subject', () => MicroservicesService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = true; + + afterEach(() => $sandbox.restore()); + + describe('.listMicroservices()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const flowId = 10; + + const response = [ + { + uuid: 'testUuid' + } + ]; + + def('subject', () => $subject.listMicroservices(flowId, user, isCLI, transaction)); + def('findMicroservicesResponse', () => Promise.resolve(response)); + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findAllExcludeFields').returns($findMicroservicesResponse); + }); + + it('calls MicroserviceManager#findAllExcludeFields() with correct args', async () => { + await $subject; + const where = isCLI ? {delete: false} : {flowId: flowId, delete: false}; + + expect(MicroserviceManager.findAllExcludeFields).to.have.been.calledWith(where, transaction); + }); + + context('when MicroserviceManager#findAllExcludeFields() fails', () => { + def('findMicroservicesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findAllExcludeFields() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('microservices') + }) + }) + }); + + describe('.getMicroservice()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const microserviceUuid = 'testMicroserviceUuid'; + + const response = { + uuid: 'testUuid' + }; + + def('subject', () => $subject.getMicroservice(microserviceUuid, user, isCLI, transaction)); + def('findMicroserviceResponse', () => Promise.resolve(response)); + + beforeEach(() => { + $sandbox.stub(MicroserviceManager, 'findOneExcludeFields').returns($findMicroserviceResponse); + }); + + it('calls MicroserviceManager#findOneExcludeFields() with correct args', async () => { + await $subject; + expect(MicroserviceManager.findOneExcludeFields).to.have.been.calledWith({ + uuid: microserviceUuid, delete: false + }, transaction); + }); + + context('when MicroserviceManager#findOneExcludeFields() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findOneExcludeFields() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('uuid') + }) + }) + }); + + describe('.createMicroservice()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const microserviceData = { + "name": "name2", + "config": "string", + "catalogItemId": 15, + "flowId": 16, + "iofogUuid": 'testIofogUuid', + "rootHostAccess": true, + "logSize": 0, + "volumeMappings": [ + { + "hostDestination": "/var/dest", + "containerDestination": "/var/dest", + "accessMode": "rw" + } + ], + "ports": [ + { + "internal": 1, + "external": 1, + "publicMode": false + } + ], + "routes": [] + }; + + const newMicroserviceUuid = 'newMicroserviceUuid'; + const newMicroservice = { + uuid: newMicroserviceUuid, + name: microserviceData.name, + config: microserviceData.config, + catalogItemId: microserviceData.catalogItemId, + flowId: microserviceData.flowId, + iofogUuid: microserviceData.iofogUuid, + rootHostAccess: microserviceData.rootHostAccess, + logSize: microserviceData.logLimit, + userId: user.id + }; + + const item = {}; + + const portMappingData = + { + "internal": 1, + "external": 1, + "publicMode": false + }; + + const mappings = []; + for (const volumeMapping of microserviceData.volumeMappings) { + const mapping = Object.assign({}, volumeMapping); + mapping.microserviceUuid = microserviceData.uuid; + mappings.push(mapping); + } + + const mappingData = { + isPublic: false, + portInternal: portMappingData.internal, + portExternal: portMappingData.external, + userId: microserviceData.userId, + microserviceUuid: microserviceData.uuid + }; + + def('subject', () => $subject.createMicroservice(microserviceData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('validatorResponse2', () => Promise.resolve(true)); + def('generateRandomStringResponse', () => newMicroserviceUuid); + def('deleteUndefinedFieldsResponse', () => newMicroservice); + def('findMicroserviceResponse', () => Promise.resolve()); + def('findMicroserviceResponse2', () => Promise.resolve(microserviceData)); + def('getCatalogItemResponse', () => Promise.resolve()); + def('getFlowResponse', () => Promise.resolve()); + def('getIoFogResponse', () => Promise.resolve()); + def('createMicroserviceResponse', () => Promise.resolve(microserviceData)); + def('findMicroservicePortResponse', () => Promise.resolve()); + def('createMicroservicePortResponse', () => Promise.resolve()); + def('updateMicroserviceResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + def('createVolumeMappingResponse', () => Promise.resolve()); + def('createMicroserviceStatusResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate') + .onFirstCall().returns($validatorResponse) + .onSecondCall().returns($validatorResponse2); + $sandbox.stub(AppHelper, 'generateRandomString').returns($generateRandomStringResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(MicroserviceManager, 'findOne') + .onFirstCall().returns($findMicroserviceResponse) + .onSecondCall().returns($findMicroserviceResponse2); + $sandbox.stub(CatalogService, 'getCatalogItem').returns($getCatalogItemResponse); + $sandbox.stub(FlowService, 'getFlow').returns($getFlowResponse); + $sandbox.stub(ioFogService, 'getFog').returns($getIoFogResponse); + $sandbox.stub(MicroserviceManager, 'create').returns($createMicroserviceResponse); + $sandbox.stub(MicroservicePortManager, 'findOne').returns($findMicroservicePortResponse); + $sandbox.stub(MicroservicePortManager, 'create').returns($createMicroservicePortResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + $sandbox.stub(VolumeMappingManager, 'bulkCreate').returns($createVolumeMappingResponse); + $sandbox.stub(MicroserviceStatusManager, 'create').returns($createMicroserviceStatusResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(microserviceData, + Validator.schemas.microserviceCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#generateRandomString() with correct args', async () => { + await $subject; + expect(AppHelper.generateRandomString).to.have.been.calledWith(32); + }); + + context('when AppHelper#generateRandomString() fails', () => { + def('generateRandomStringResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }); + + context('when AppHelper#generateRandomString() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(newMicroservice); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + const err = 'Invalid microservice UUID \'undefined\''; + def('deleteUndefinedFieldsResponse', () => Promise.reject(err)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls MicroserviceManager#findOne() with correct args', async () => { + await $subject; + const where = item.id + ? + { + name: microserviceData.name, + uuid: {[Op.ne]: item.id}, + userId: user.id + } + : + { + name: microserviceData.name, + userId: user.id + }; + expect(MicroserviceManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when MicroserviceManager#findOne() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findOne() succeeds', () => { + it('calls CatalogService#getCatalogItem() with correct args', async () => { + await $subject; + expect(CatalogService.getCatalogItem).to.have.been.calledWith(newMicroservice.catalogItemId, + user, isCLI, transaction); + }); + + context('when CatalogService#getCatalogItem() fails', () => { + def('getCatalogItemResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when CatalogService#getCatalogItem() succeeds', () => { + it('calls FlowService#getFlow() with correct args', async () => { + await $subject; + expect(FlowService.getFlow).to.have.been.calledWith(newMicroservice.flowId, + user, isCLI, transaction); + }); + + context('when FlowService#getFlow() fails', () => { + def('getFlowResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when FlowService#getFlow() succeeds', () => { + it('calls IoFogService#getFog() with correct args', async () => { + await $subject; + expect(ioFogService.getFog).to.have.been.calledWith({ + uuid: newMicroservice.iofogUuid + }, user, isCLI, transaction); + }); + + context('when IoFogService#getFog() fails', () => { + def('getIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when IoFogService#getFog() succeeds', () => { + it('calls MicroserviceManager#create() with correct args', async () => { + await $subject; + expect(MicroserviceManager.create).to.have.been.calledWith(newMicroservice, + transaction); + }); + + context('when MicroserviceManager#create() fails', () => { + def('getIoFogResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#create() succeeds', () => { + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(portMappingData, Validator.schemas.portsCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse2', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls MicroserviceManager#findOne() with correct args', async () => { + await $subject; + const where = isCLI + ? {uuid: microserviceData.uuid} + : {uuid: microserviceData.uuid, userId: user.id}; + expect(MicroserviceManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when MicroserviceManager#findOne() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findOne() succeeds', () => { + it('calls MicroservicePortManager#findOne() with correct args', async () => { + await $subject; + expect(MicroservicePortManager.findOne).to.have.been.calledWith({ + microserviceUuid: microserviceData.uuid, + [Op.or]: + [ + { + portInternal: portMappingData.internal + }, + { + portExternal: portMappingData.external + } + ] + }, transaction); + }); + + context('when MicroservicePortManager#findOne() fails', () => { + def('findMicroservicePortResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroservicePortManager#findOne() succeeds', () => { + it('calls MicroservicePortManager#create() with correct args', async () => { + await $subject; + expect(MicroservicePortManager.create).to.have.been.calledWith(mappingData, transaction); + }); + + context('when MicroservicePortManager#create() fails', () => { + def('createMicroservicePortResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroservicePortManager#create() succeeds', () => { + it('calls MicroserviceManager#update() with correct args', async () => { + await $subject; + const updateRebuildMs = { + rebuild: true + }; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microserviceData.uuid + }, updateRebuildMs, transaction); + }); + + context('when MicroserviceManager#update() fails', () => { + def('updateMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microserviceData.iofogUuid, + ChangeTrackingService.events.microserviceConfig, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('calls VolumeMappingManager#bulkCreate() with correct args', async () => { + await $subject; + expect(VolumeMappingManager.bulkCreate).to.have.been.calledWith(mappings, + transaction); + }); + + context('when VolumeMappingManager#bulkCreate() fails', () => { + def('createVolumeMappingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when VolumeMappingManager#bulkCreate() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microserviceData.iofogUuid, + ChangeTrackingService.events.microserviceList, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('calls MicroserviceStatusManager#create() with correct args', async () => { + await $subject; + expect(MicroserviceStatusManager.create).to.have.been.calledWith({ + microserviceUuid: microserviceData.uuid + }, transaction); + }); + + context('when MicroserviceStatusManager#create() fails', () => { + def('createMicroserviceStatusResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceStatusManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('uuid'); + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }) + }); +// +// describe('.updateMicroservice()', () => { +// const transaction = {}; +// const error = 'Error!'; +// +// const user = { +// id: 15 +// }; +// +// const microserviceUuid = 'testMicroserviceUuid'; +// +// const query = isCLI +// ? +// { +// uuid: microserviceUuid +// } +// : +// { +// uuid: microserviceUuid, +// userId: user.id +// }; +// +// const microserviceData = { +// "name": "name2", +// "config": "string", +// "catalogItemId": 15, +// "flowId": 16, +// "iofogUuid": 'testIofogUuid', +// "rootHostAccess": true, +// "logSize": 0, +// "volumeMappings": [ +// { +// "hostDestination": "/var/dest", +// "containerDestination": "/var/dest", +// "accessMode": "rw" +// } +// ], +// "ports": [ +// { +// "internal": 1, +// "external": 1, +// "publicMode": false +// } +// ], +// "routes": [] +// }; +// +// const microserviceToUpdate = { +// name: microserviceData.name, +// config: microserviceData.config, +// rebuild: microserviceData.rebuild, +// iofogUuid: microserviceData.iofogUuid, +// rootHostAccess: microserviceData.rootHostAccess, +// logSize: microserviceData.logLimit, +// volumeMappings: microserviceData.volumeMappings +// }; +// +// const newMicroserviceUuid = 'newMicroserviceUuid'; +// const newMicroservice = { +// uuid: newMicroserviceUuid, +// name: microserviceData.name, +// config: microserviceData.config, +// catalogItemId: microserviceData.catalogItemId, +// flowId: microserviceData.flowId, +// iofogUuid: microserviceData.iofogUuid, +// rootHostAccess: microserviceData.rootHostAccess, +// logSize: microserviceData.logLimit, +// userId: user.id +// }; +// +// const item = {}; +// +// const portMappingData = +// { +// "internal": 1, +// "external": 1, +// "publicMode": false +// }; +// +// const mappings = []; +// for (const volumeMapping of microserviceData.volumeMappings) { +// const mapping = Object.assign({}, volumeMapping); +// mapping.microserviceUuid = microserviceData.uuid; +// mappings.push(mapping); +// } +// +// const mappingData = { +// isPublic: false, +// portInternal: portMappingData.internal, +// portExternal: portMappingData.external, +// userId: microserviceData.userId, +// microserviceUuid: microserviceData.uuid +// }; +// +// def('subject', () => $subject.updateMicroservice(microserviceUuid, microserviceData, user, isCLI, transaction)); +// def('validatorResponse', () => Promise.resolve(true)); +// def('deleteUndefinedFieldsResponse', () => newMicroservice); +// def('findMicroserviceResponse', () => Promise.resolve()); +// def('findMicroserviceResponse2', () => Promise.resolve(microserviceData)); +// def('getIoFogResponse', () => Promise.resolve()); +// def('updateMicroserviceResponse', () => Promise.resolve()); +// def('updateVolumeMappingResponse', () => Promise.resolve()); +// // TODO +// +// def('updateChangeTrackingResponse', () => Promise.resolve()); +// def('updateChangeTrackingResponse2', () => Promise.resolve()); +// def('updateChangeTrackingResponse3', () => Promise.resolve()); +// +// beforeEach(() => { +// $sandbox.stub(Validator, 'validate').returns($validatorResponse); +// $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); +// $sandbox.stub(MicroserviceManager, 'findOne') +// .onFirstCall().returns($findMicroserviceResponse) +// .onSecondCall().returns($findMicroserviceResponse2); +// $sandbox.stub(ioFogService, 'getFog').returns($getIoFogResponse); +// $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); +// $sandbox.stub(VolumeMappingManager, 'update').returns($updateVolumeMappingResponse); +// $sandbox.stub(RoutingManager, 'findAll').returns($findAllRoutesResponse); +// +// // TODO +// // delete route endpoint +// // create route endpoint +// $sandbox.stub(ChangeTrackingService, 'update') +// .onFirstCall().returns($updateChangeTrackingResponse) +// .onSecondCall().returns($updateChangeTrackingResponse2) +// .onThirdCall().returns($updateChangeTrackingResponse3); +// }); +// +// it('calls Validator#validate() with correct args', async () => { +// await $subject; +// expect(Validator.validate).to.have.been.calledWith(microserviceData, +// Validator.schemas.microserviceCreate); +// }); +// +// context('when Validator#validate() fails', () => { +// def('validatorResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when Validator#validate() succeeds', () => { +// it('calls AppHelper#generateRandomString() with correct args', async () => { +// await $subject; +// expect(AppHelper.generateRandomString).to.have.been.calledWith(32); +// }); +// +// context('when AppHelper#generateRandomString() fails', () => { +// def('generateRandomStringResponse', () => error); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.eventually.have.property('uuid'); +// }) +// }); +// +// context('when AppHelper#generateRandomString() succeeds', () => { +// it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { +// await $subject; +// expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(newMicroservice); +// }); +// +// context('when AppHelper#deleteUndefinedFields() fails', () => { +// const err = 'Invalid microservice UUID \'undefined\''; +// def('deleteUndefinedFieldsResponse', () => Promise.reject(err)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(err); +// }) +// }); +// +// context('when AppHelper#deleteUndefinedFields() succeeds', () => { +// it('calls MicroserviceManager#findOne() with correct args', async () => { +// await $subject; +// const where = item.id +// ? +// { +// name: microserviceData.name, +// uuid: {[Op.ne]: item.id}, +// userId: user.id +// } +// : +// { +// name: microserviceData.name, +// userId: user.id +// }; +// expect(MicroserviceManager.findOne).to.have.been.calledWith(where, transaction); +// }); +// +// context('when MicroserviceManager#findOne() fails', () => { +// def('findMicroserviceResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceManager#findOne() succeeds', () => { +// it('calls CatalogService#getCatalogItem() with correct args', async () => { +// await $subject; +// expect(CatalogService.getCatalogItem).to.have.been.calledWith(newMicroservice.catalogItemId, +// user, isCLI, transaction); +// }); +// +// context('when CatalogService#getCatalogItem() fails', () => { +// def('getCatalogItemResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when CatalogService#getCatalogItem() succeeds', () => { +// it('calls FlowService#getFlow() with correct args', async () => { +// await $subject; +// expect(FlowService.getFlow).to.have.been.calledWith(newMicroservice.flowId, +// user, isCLI, transaction); +// }); +// +// context('when FlowService#getFlow() fails', () => { +// def('getFlowResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when FlowService#getFlow() succeeds', () => { +// it('calls IoFogService#getFog() with correct args', async () => { +// await $subject; +// expect(ioFogService.getFog).to.have.been.calledWith({ +// uuid: newMicroservice.iofogUuid +// }, user, isCLI, transaction); +// }); +// +// context('when IoFogService#getFog() fails', () => { +// def('getIoFogResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when IoFogService#getFog() succeeds', () => { +// it('calls MicroserviceManager#create() with correct args', async () => { +// await $subject; +// expect(MicroserviceManager.create).to.have.been.calledWith(newMicroservice, +// transaction); +// }); +// +// context('when MicroserviceManager#create() fails', () => { +// def('getIoFogResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceManager#create() succeeds', () => { +// it('calls Validator#validate() with correct args', async () => { +// await $subject; +// expect(Validator.validate).to.have.been.calledWith(portMappingData, Validator.schemas.portsCreate); +// }); +// +// context('when Validator#validate() fails', () => { +// def('validatorResponse2', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when Validator#validate() succeeds', () => { +// it('calls MicroserviceManager#findOne() with correct args', async () => { +// await $subject; +// const where = isCLI +// ? {uuid: microserviceData.uuid} +// : {uuid: microserviceData.uuid, userId: user.id}; +// expect(MicroserviceManager.findOne).to.have.been.calledWith(where, transaction); +// }); +// +// context('when MicroserviceManager#findOne() fails', () => { +// def('findMicroserviceResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceManager#findOne() succeeds', () => { +// it('calls MicroservicePortManager#findOne() with correct args', async () => { +// await $subject; +// expect(MicroservicePortManager.findOne).to.have.been.calledWith({ +// microserviceUuid: microserviceData.uuid, +// [Op.or]: +// [ +// { +// portInternal: portMappingData.internal +// }, +// { +// portExternal: portMappingData.external +// } +// ] +// }, transaction); +// }); +// +// context('when MicroservicePortManager#findOne() fails', () => { +// def('findMicroservicePortResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroservicePortManager#findOne() succeeds', () => { +// it('calls MicroservicePortManager#create() with correct args', async () => { +// await $subject; +// expect(MicroservicePortManager.create).to.have.been.calledWith(mappingData, transaction); +// }); +// +// context('when MicroservicePortManager#create() fails', () => { +// def('createMicroservicePortResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroservicePortManager#create() succeeds', () => { +// it('calls MicroserviceManager#update() with correct args', async () => { +// await $subject; +// const updateRebuildMs = { +// rebuild: true +// }; +// expect(MicroserviceManager.update).to.have.been.calledWith({ +// uuid: microserviceData.uuid +// }, updateRebuildMs, transaction); +// }); +// +// context('when MicroserviceManager#update() fails', () => { +// def('updateMicroserviceResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceManager#update() succeeds', () => { +// it('calls ChangeTrackingService#update() with correct args', async () => { +// await $subject; +// expect(ChangeTrackingService.update).to.have.been.calledWith(microserviceData.iofogUuid, +// ChangeTrackingService.events.microserviceConfig, transaction); +// }); +// +// context('when ChangeTrackingService#update() fails', () => { +// def('updateChangeTrackingResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when ChangeTrackingService#update() succeeds', () => { +// it('calls VolumeMappingManager#bulkCreate() with correct args', async () => { +// await $subject; +// expect(VolumeMappingManager.bulkCreate).to.have.been.calledWith(mappings, +// transaction); +// }); +// +// context('when VolumeMappingManager#bulkCreate() fails', () => { +// def('createVolumeMappingResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when VolumeMappingManager#bulkCreate() succeeds', () => { +// it('calls ChangeTrackingService#update() with correct args', async () => { +// await $subject; +// expect(ChangeTrackingService.update).to.have.been.calledWith(microserviceData.iofogUuid, +// ChangeTrackingService.events.microserviceList, transaction); +// }); +// +// context('when ChangeTrackingService#update() fails', () => { +// def('updateChangeTrackingResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when ChangeTrackingService#update() succeeds', () => { +// it('calls MicroserviceStatusManager#create() with correct args', async () => { +// await $subject; +// expect(MicroserviceStatusManager.create).to.have.been.calledWith({ +// microserviceUuid: microserviceData.uuid +// }, transaction); +// }); +// +// context('when MicroserviceStatusManager#create() fails', () => { +// def('createMicroserviceStatusResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceStatusManager#create() succeeds', () => { +// it('fulfills the promise', () => { +// return expect($subject).to.eventually.have.property('uuid'); +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }); +// +// +// describe('.deleteMicroservice()', () => { +// const transaction = {}; +// const error = 'Error!'; +// +// const user = { +// id: 15 +// }; +// +// const microserviceUuid = 'testMicroserviceUuid'; +// +// const microserviceData = { +// "name": "name2", +// "config": "string", +// "catalogItemId": 15, +// "flowId": 16, +// "iofogUuid": 'testIofogUuid', +// "rootHostAccess": true, +// "logSize": 0, +// "volumeMappings": [ +// { +// "hostDestination": "/var/dest", +// "containerDestination": "/var/dest", +// "accessMode": "rw" +// } +// ], +// "ports": [ +// { +// "internal": 1, +// "external": 1, +// "publicMode": false +// } +// ], +// "routes": [] +// }; +// +// const portMappingData = [ +// { +// "internal": 1, +// "external": 1, +// "publicMode": false +// } +// ]; +// +// +// const where = isCLI +// ? +// { +// uuid: microserviceUuid, +// } +// : +// { +// uuid: microserviceUuid, +// userId: user.id +// }; +// +// const mappingData = { +// isPublic: false, +// portInternal: portMappingData.internal, +// portExternal: portMappingData.external, +// userId: microserviceData.userId, +// microserviceUuid: microserviceData.uuid +// }; +// +// def('subject', () => $subject.deleteMicroservice(microserviceUuid, microserviceData, user, isCLI, transaction)); +// def('findMicroserviceResponse', () => Promise.resolve(microserviceData)); +// def('findMicroservicePortResponse', () => Promise.resolve()); +// def('deleteMicroservicePortResponse', () => Promise.resolve()); +// def('updateMicroserviceResponse', () => Promise.resolve()); +// def('updateChangeTrackingResponse', () => Promise.resolve()); +// +// beforeEach(() => { +// $sandbox.stub(MicroserviceManager, 'findOneWithStatus').returns($findMicroserviceResponse); +// $sandbox.stub(MicroservicePortManager, 'findAll').returns($findMicroservicePortResponse); +// $sandbox.stub(MicroservicePortManager, 'delete').returns($deleteMicroservicePortResponse); +// $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); +// $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); +// }); +// +// it('calls Validator#validate() with correct args', async () => { +// await $subject; +// expect(Validator.validate).to.have.been.calledWith(portMappingData, Validator.schemas.portsCreate); +// }); +// +// context('when Validator#validate() fails', () => { +// def('validatorResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when Validator#validate() succeeds', () => { +// it('calls MicroserviceManager#findOne() with correct args', async () => { +// await $subject; +// expect(MicroserviceManager.findOne).to.have.been.calledWith(where, transaction); +// }); +// +// context('when MicroserviceManager#findOne() fails', () => { +// def('findMicroserviceResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceManager#findOne() succeeds', () => { +// it('calls MicroservicePortManager#findOne() with correct args', async () => { +// await $subject; +// expect(MicroservicePortManager.findOne).to.have.been.calledWith({ +// microserviceUuid: microserviceUuid, +// [Op.or]: +// [ +// { +// portInternal: portMappingData.internal +// }, +// { +// portExternal: portMappingData.external +// } +// ] +// }, transaction); +// }); +// +// context('when MicroservicePortManager#findOne() fails', () => { +// def('findMicroservicePortResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroservicePortManager#findOne() succeeds', () => { +// it('calls MicroservicePortManager#create() with correct args', async () => { +// await $subject; +// expect(MicroservicePortManager.create).to.have.been.calledWith(mappingData, transaction); +// }); +// +// context('when MicroservicePortManager#create() fails', () => { +// def('createMicroservicePortResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroservicePortManager#create() succeeds', () => { +// it('calls MicroserviceManager#update() with correct args', async () => { +// await $subject; +// const updateRebuildMs = { +// rebuild: true +// }; +// expect(MicroserviceManager.update).to.have.been.calledWith({ +// uuid: microserviceData.uuid +// }, updateRebuildMs, transaction); +// }); +// +// context('when MicroserviceManager#update() fails', () => { +// def('updateMicroserviceResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when MicroserviceManager#update() succeeds', () => { +// it('calls ChangeTrackingService#update() with correct args', async () => { +// await $subject; +// expect(ChangeTrackingService.update).to.have.been.calledWith(microserviceData.iofogUuid, +// ChangeTrackingService.events.microserviceConfig, transaction); +// }); +// +// context('when ChangeTrackingService#update() fails', () => { +// def('updateChangeTrackingResponse', () => Promise.reject(error)); +// +// it(`fails with ${error}`, () => { +// return expect($subject).to.be.rejectedWith(error); +// }) +// }); +// +// context('when ChangeTrackingService#update() succeeds', () => { +// it('fulfills the promise', () => { +// return expect($subject).eventually.equals(undefined); +// }) +// }) +// }) +// }) +// }) +// }) +// }) +// }); +// +// }); +// + + describe('.createPortMapping()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const microserviceUuid = 'testMicroserviceUuid'; + + const microserviceData = { + "name": "name2", + "config": "string", + "catalogItemId": 15, + "flowId": 16, + "iofogUuid": 'testIofogUuid', + "rootHostAccess": true, + "logSize": 0, + "volumeMappings": [ + { + "hostDestination": "/var/dest", + "containerDestination": "/var/dest", + "accessMode": "rw" + } + ], + "ports": [ + { + "internal": 1, + "external": 1, + "publicMode": false + } + ], + "routes": [] + }; + + const portMappingData = [ + { + "internal": 1, + "external": 1, + "publicMode": false + } + ]; + + const where = isCLI + ? {uuid: microserviceUuid} + : {uuid: microserviceUuid, userId: user.id}; + + const mappingData = { + isPublic: false, + portInternal: portMappingData.internal, + portExternal: portMappingData.external, + userId: microserviceData.userId, + microserviceUuid: microserviceData.uuid + }; + + def('subject', () => $subject.createPortMapping(microserviceUuid, portMappingData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findMicroserviceResponse', () => Promise.resolve(microserviceData)); + def('findMicroservicePortResponse', () => Promise.resolve()); + def('createMicroservicePortResponse', () => Promise.resolve()); + def('updateMicroserviceResponse', () => Promise.resolve()); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(MicroserviceManager, 'findOne').returns($findMicroserviceResponse); + $sandbox.stub(MicroservicePortManager, 'findOne').returns($findMicroservicePortResponse); + $sandbox.stub(MicroservicePortManager, 'create').returns($createMicroservicePortResponse); + $sandbox.stub(MicroserviceManager, 'update').returns($updateMicroserviceResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(portMappingData, Validator.schemas.portsCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls MicroserviceManager#findOne() with correct args', async () => { + await $subject; + expect(MicroserviceManager.findOne).to.have.been.calledWith(where, transaction); + }); + + context('when MicroserviceManager#findOne() fails', () => { + def('findMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#findOne() succeeds', () => { + it('calls MicroservicePortManager#findOne() with correct args', async () => { + await $subject; + expect(MicroservicePortManager.findOne).to.have.been.calledWith({ + microserviceUuid: microserviceUuid, + [Op.or]: + [ + { + portInternal: portMappingData.internal + }, + { + portExternal: portMappingData.external + } + ] + }, transaction); + }); + + context('when MicroservicePortManager#findOne() fails', () => { + def('findMicroservicePortResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroservicePortManager#findOne() succeeds', () => { + it('calls MicroservicePortManager#create() with correct args', async () => { + await $subject; + expect(MicroservicePortManager.create).to.have.been.calledWith(mappingData, transaction); + }); + + context('when MicroservicePortManager#create() fails', () => { + def('createMicroservicePortResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroservicePortManager#create() succeeds', () => { + it('calls MicroserviceManager#update() with correct args', async () => { + await $subject; + const updateRebuildMs = { + rebuild: true + }; + expect(MicroserviceManager.update).to.have.been.calledWith({ + uuid: microserviceData.uuid + }, updateRebuildMs, transaction); + }); + + context('when MicroserviceManager#update() fails', () => { + def('updateMicroserviceResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when MicroserviceManager#update() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(microserviceData.iofogUuid, + ChangeTrackingService.events.microserviceConfig, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('updateChangeTrackingResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).eventually.equals(undefined); + }) + }) + }) + }) + }) + }) + }) + }); + +}); \ No newline at end of file diff --git a/test/src/services/registry-service.test.js b/test/src/services/registry-service.test.js new file mode 100644 index 000000000..111a99bd0 --- /dev/null +++ b/test/src/services/registry-service.test.js @@ -0,0 +1,486 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const RegistryManager = require('../../../src/sequelize/managers/registry-manager'); +const RegistryService = require('../../../src/services/registry-service'); +const Validator = require('../../../src/schemas'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ioFogManager = require('../../../src/sequelize/managers/iofog-manager'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; + +describe('Registry Service', () => { + def('subject', () => RegistryService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.createRegistry()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const registry = { + url: 'testUrl', + username: 'testUsername', + password: 'testPassword', + isPublic: false, + userEmail: 'testEmail', + requiresCert: false, + certificate: 'testCertificate', + userId: user.id + }; + + const registryCreate = { + url: registry.url, + username: registry.username, + password: registry.password, + isPublic: registry.isPublic, + userEmail: registry.email, + requiresCert: registry.requiresCert, + certificate: registry.certificate, + userId: user.id + }; + + const ioFogs = [{ + uuid: 'testUuid' + }]; + + def('subject', () => $subject.createRegistry(registry, user, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => registryCreate); + def('createRegistryResponse', () => Promise.resolve({ + id: 16 + })); + def('findIoFogsResponse', () => Promise.resolve(ioFogs)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(RegistryManager, 'create').returns($createRegistryResponse); + $sandbox.stub(ioFogManager, 'findAll').returns($findIoFogsResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(registry, Validator.schemas.registryCreate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(registryCreate); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('id'); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls RegistryManager#create() with correct args', async () => { + await $subject; + expect(RegistryManager.create).to.have.been.calledWith(registryCreate, transaction); + }); + + context('when RegistryManager#create() fails', () => { + def('createRegistryResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when RegistryManager#create() succeeds', () => { + it('calls ioFogManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogManager.findAll).to.have.been.calledWith({ + userId: user.id + }, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findIoFogsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(ioFogs[0].uuid, + ChangeTrackingService.events.registries, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('findIoFogsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('id'); + }) + }) + }) + }) + }) + }) + }); + + describe('.findRegistries()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const queryRegistry = isCLI + ? {} + : { + [Op.or]: + [ + { + userId: user.id + }, + { + isPublic: true + } + ] + }; + + def('subject', () => $subject.findRegistries(user, isCLI, transaction)); + def('findRegistriesResponse', () => Promise.resolve([])); + + beforeEach(() => { + $sandbox.stub(RegistryManager, 'findAll').returns($findRegistriesResponse); + }); + + it('calls RegistryManager#findAll() with correct args', async () => { + await $subject; + expect(RegistryManager.findAll).to.have.been.calledWith(queryRegistry, transaction); + }); + + context('when RegistryManager#findAll() fails', () => { + def('findRegistriesResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when RegistryManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('registries'); + }) + }) + }); + + describe('.deleteRegistry()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const registryData = { + id: 5 + }; + + const queryData = isCLI + ? {id: registryData.id} + : {id: registryData.id, userId: user.id}; + + const ioFogs = [{ + uuid: 'testUuid' + }]; + + def('subject', () => $subject.deleteRegistry(registryData, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findRegistryResponse', () => Promise.resolve({ + userId: user.id + })); + def('deleteRegistryResponse', () => Promise.resolve()); + def('findIoFogsResponse', () => Promise.resolve(ioFogs)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(RegistryManager, 'findOne').returns($findRegistryResponse); + $sandbox.stub(RegistryManager, 'delete').returns($deleteRegistryResponse); + $sandbox.stub(ioFogManager, 'findAll').returns($findIoFogsResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(registryData, Validator.schemas.registryDelete); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls RegistryManager#findOne() with correct args', async () => { + await $subject; + expect(RegistryManager.findOne).to.have.been.calledWith(queryData, transaction); + }); + + context('when RegistryManager#findOne() fails', () => { + def('findRegistryResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when RegistryManager#findOne() succeeds', () => { + it('calls RegistryManager#delete() with correct args', async () => { + await $subject; + expect(RegistryManager.delete).to.have.been.calledWith(queryData, transaction); + }); + + context('when RegistryManager#delete() fails', () => { + def('deleteRegistryResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when RegistryManager#delete() succeeds', () => { + it('calls ioFogManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogManager.findAll).to.have.been.calledWith({ + userId: user.id + }, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findIoFogsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(ioFogs[0].uuid, + ChangeTrackingService.events.registries, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('findIoFogsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }); + + describe('.updateRegistry()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const registryId = 5; + + const registry = { + url: 'testUrl', + username: 'testUsername', + password: 'testPassword', + isPublic: false, + userEmail: 'testEmail', + requiresCert: false, + certificate: 'testCertificate', + userId: user.id + }; + + let registryUpdate = { + url: registry.url, + username: registry.username, + password: registry.password, + isPublic: registry.isPublic, + userEmail: registry.email, + requiresCert: registry.requiresCert, + certificate: registry.certificate + }; + + const ioFogs = [{ + uuid: 'testUuid' + }]; + + const where = isCLI ? + { + id: registryId + } + : + { + id: registryId, + userId: user.id + }; + + def('subject', () => $subject.updateRegistry(registry, registryId, user, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findRegistryResponse', () => Promise.resolve({})); + def('deleteUndefinedFieldsResponse', () => registryUpdate); + def('updateRegistryResponse', () => Promise.resolve()); + def('findIoFogsResponse', () => Promise.resolve(ioFogs)); + def('updateChangeTrackingResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(RegistryManager, 'findOne').returns($findRegistryResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(RegistryManager, 'update').returns($updateRegistryResponse); + $sandbox.stub(ioFogManager, 'findAll').returns($findIoFogsResponse); + $sandbox.stub(ChangeTrackingService, 'update').returns($updateChangeTrackingResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(registry, Validator.schemas.registryUpdate); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error) + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls RegistryManager#findOne() with correct args', async () => { + await $subject; + expect(RegistryManager.findOne).to.have.been.calledWith({ + id: registryId + }, transaction); + }); + + context('when RegistryManager#findOne() fails', () => { + def('findRegistryResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when RegistryManager#findOne() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(registryUpdate); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls RegistryManager#update() with correct args', async () => { + await $subject; + expect(RegistryManager.update).to.have.been.calledWith(where, registryUpdate, transaction); + }); + + context('when RegistryManager#update() fails', () => { + def('updateRegistryResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when RegistryManager#update() succeeds', () => { + it('calls ioFogManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogManager.findAll).to.have.been.calledWith({ + userId: user.id + }, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findIoFogsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls ChangeTrackingService#update() with correct args', async () => { + await $subject; + expect(ChangeTrackingService.update).to.have.been.calledWith(ioFogs[0].uuid, + ChangeTrackingService.events.registries, transaction); + }); + + context('when ChangeTrackingService#update() fails', () => { + def('findIoFogsResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ChangeTrackingService#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }) + }) + }); + +}); \ No newline at end of file diff --git a/test/src/services/user-service.test.js b/test/src/services/user-service.test.js new file mode 100644 index 000000000..093213ed9 --- /dev/null +++ b/test/src/services/user-service.test.js @@ -0,0 +1,694 @@ +const {expect} = require('chai'); +const sinon = require('sinon'); + +const UserManager = require('../../../src/sequelize/managers/user-manager'); +const UserService = require('../../../src/services/user-service'); +const Config = require('../../../src/config'); +const AccessTokenService = require('../../../src/services/access-token-service'); +const Validator = require('../../../src/schemas'); +const AppHelper = require('../../../src/helpers/app-helper'); +const ioFogManager = require('../../../src/sequelize/managers/iofog-manager'); +const ChangeTrackingService = require('../../../src/services/change-tracking-service'); +const EmailActivationCodeService = require('../../../src/services/email-activation-code-service'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; +const ErrorMessages = require('../../../src/helpers/error-messages'); +const nodemailer = require('nodemailer'); + +describe('User Service', () => { + def('subject', () => UserService); + def('sandbox', () => sinon.createSandbox()); + + const isCLI = false; + + afterEach(() => $sandbox.restore()); + + describe('.signUp()', () => { + const transaction = {}; + const error = 'Error!'; + + const newUser = { + id: 16, + firstName: 'testFirstName', + lastName: 'testLastName', + email: 'testEmail', + emailActivated: true + }; + + const response = { + userId: 16, + firstName: newUser.firstName, + lastName: newUser.lastName, + email: newUser.email, + emailActivated: newUser.emailActivated + }; + + def('subject', () => $subject.signUp(newUser, isCLI, transaction)); + def('configGetResponse', () => false); + def('findUserResponse', () => Promise.resolve()); + def('createUserResponse', () => Promise.resolve(newUser)); + + beforeEach(() => { + $sandbox.stub(Config, 'get').returns($configGetResponse); + $sandbox.stub(UserManager, 'findOne').returns($findUserResponse); + $sandbox.stub(UserManager, 'create').returns($createUserResponse); + }); + + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith("Email:ActivationEnabled"); + }); + + context('when Config#get() succeeds', () => { + it('calls UserManager#findOne() with correct args', async () => { + await $subject; + expect(UserManager.findOne).to.have.been.calledWith({ + email: newUser.email + }, transaction); + }); + + context('when UserManager#findOne() fails', () => { + def('findUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findOne() succeeds', () => { + it('calls UserManager#create() with correct args', async () => { + await $subject; + expect(UserManager.create).to.have.been.calledWith(newUser, transaction); + }); + + context('when UserManager#create() fails', () => { + def('createUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#create() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.deep.equal(response); + }) + }) + }) + }) + }); + + describe('.login()', () => { + const transaction = {}; + const error = 'Error!'; + + const credentials = { + email: 'testEmail', + password: 'testPassword' + }; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const testAccessToken = 'testAccessToken'; + + const configGet2 = 155; + const date = 1555; + const tokenExpireTime = date + (configGet2 * 1000); + + const createToken = { + token: testAccessToken, + expirationTime: tokenExpireTime, + userId: user.id + }; + + + def('subject', () => $subject.login(credentials, isCLI, transaction)); + def('findUserResponse', () => Promise.resolve(user)); + def('decryptTextResponse', () => credentials.password); + def('getConfigResponse', () => false); + def('getConfigResponse2', () => configGet2); + def('generateAccessTokenResponse', () => 'testAccessToken'); + def('findByAccessTokenResponse', () => false); + def('createAccessTokenResponse', () => Promise.resolve({ + token: 'token' + })); + def('dateResponse', () => date); + + beforeEach(() => { + $sandbox.stub(UserManager, 'findOne').returns($findUserResponse); + $sandbox.stub(AppHelper, 'decryptText').returns($decryptTextResponse); + $sandbox.stub(Config, 'get') + .onFirstCall().returns($getConfigResponse) + .onSecondCall().returns($getConfigResponse2); + $sandbox.stub(AppHelper, 'generateAccessToken').returns($generateAccessTokenResponse); + $sandbox.stub(UserManager, 'findByAccessToken').returns($findByAccessTokenResponse); + $sandbox.stub(AccessTokenService, 'createAccessToken').returns($createAccessTokenResponse); + $sandbox.stub(Date.prototype, 'getTime').returns($dateResponse); + }); + + it('calls UserManager#findOne() with correct args', async () => { + await $subject; + expect(UserManager.findOne).to.have.been.calledWith({ + email: credentials.email + }, transaction); + }); + + context('when UserManager#findOne() fails', () => { + def('findUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findOne() succeeds', () => { + it('calls AppHelper#decryptText() with correct args', async () => { + await $subject; + expect(AppHelper.decryptText).to.have.been.calledWith(user.password, user.email); + }); + + context('when AppHelper#decryptText() fails', () => { + const err = 'Invalid credentials'; + def('decryptTextResponse', () => Promise.reject(err)); + + it(`fails with ${err}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when AppHelper#decryptText() succeeds', () => { + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith("Email:ActivationEnabled"); + }); + + context('when Config#get() fails', () => { + const err = 'Email is not activated. Please activate your account first.'; + def('getConfigResponse', () => Promise.reject(err)); + + it(`fails with ${err}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when Config#get() succeeds', () => { + it('calls AppHelper#generateAccessToken() with correct args', async () => { + await $subject; + expect(AppHelper.generateAccessToken).to.have.been.calledWith(); + }); + + context('when AppHelper#generateAccessToken() fails', () => { + def('generateAccessTokenResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('accessToken'); + }) + }); + + context('when AppHelper#generateAccessToken() succeeds', () => { + it('calls UserManager#findByAccessToken() with correct args', async () => { + await $subject; + expect(UserManager.findByAccessToken).to.have.been.calledWith(testAccessToken, transaction); + }); + + context('when UserManager#findByAccessToken() fails', () => { + def('findByAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findByAccessToken() succeeds', () => { + it('calls Config#get() with correct args', async () => { + await $subject; + expect(Config.get).to.have.been.calledWith('Settings:UserTokenExpirationIntervalSeconds'); + }); + + context('when Config#get() fails', () => { + def('getConfigResponse2', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('accessToken'); + }) + }); + + context('when Config#get() succeeds', () => { + it('calls AccessTokenService#createAccessToken() with correct args', async () => { + await $subject; + expect(AccessTokenService.createAccessToken).to.have.been.calledWith(createToken, transaction); + }); + + context('when AccessTokenService#createAccessToken() fails', () => { + def('createAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when AccessTokenService#createAccessToken() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('accessToken'); + }) + }) + }) + }) + }) + }) + }) + }) + }); + + describe('.resendActivation()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const configGet2 = 155; + + const emailObj = { + email: 'testEmail' + }; + + def('subject', () => $subject.resendActivation(emailObj, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('findUserResponse', () => Promise.resolve(user)); + def('generateActivationCodeResponse', () => Promise.resolve()); + def('saveActivationCodeResponse', () => Promise.resolve()); + def('getConfigResponse', () => false); + def('getConfigResponse2', () => configGet2); + def('getConfigResponse3', () => configGet2); + def('getConfigResponse4', () => configGet2); + def('decryptTextResponse', () => 'testPassword'); + def('createTransportResponse', () => Promise.resolve()); + def('sendMailResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(UserManager, 'findOne').returns($findUserResponse); + $sandbox.stub(EmailActivationCodeService, 'generateActivationCode').returns($generateActivationCodeResponse); + $sandbox.stub(EmailActivationCodeService, 'saveActivationCode').returns($saveActivationCodeResponse); + $sandbox.stub(Config, 'get') + .onCall(0).returns($getConfigResponse) + .onCall(1).returns($getConfigResponse2) + .onCall(2).returns($getConfigResponse3) + .onCall(3).returns($getConfigResponse4); + $sandbox.stub(AppHelper, 'decryptText').returns($decryptTextResponse); + // TODO with rewire + // $sandbox.stub(nodemailer, 'createTransport').returns($createTransportResponse); + // $sandbox.stub(transporter, 'sendmail').returns($sendMailResponse); + }); + + // TODO finish + + }); + + + describe('.activateUser()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const updatedObj = { + emailActivated: true + }; + + const codeData = { + activationCode: 'testActivationCode' + }; + + def('subject', () => $subject.activateUser(codeData, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('verifyActivationCodeResponse', () => Promise.resolve({ + userId: user.id + })); + def('updateUserResponse', () => Promise.resolve()); + def('deleteActivationCodeResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(EmailActivationCodeService, 'verifyActivationCode').returns($verifyActivationCodeResponse); + $sandbox.stub(UserManager, 'update').returns($updateUserResponse); + $sandbox.stub(EmailActivationCodeService, 'deleteActivationCode').returns($deleteActivationCodeResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(codeData, Validator.schemas.activateUser); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls EmailActivationCodeService#verifyActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeService.verifyActivationCode).to.have.been.calledWith(codeData.activationCode, transaction); + }); + + context('when EmailActivationCodeService#verifyActivationCode() fails', () => { + def('verifyActivationCodeResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when EmailActivationCodeService#verifyActivationCode() succeeds', () => { + it('calls UserManager#update() with correct args', async () => { + await $subject; + expect(UserManager.update).to.have.been.calledWith({ + id: user.id + }, updatedObj, transaction); + }); + + context('when UserManager#update() fails', () => { + const err = 'User not updated'; + def('updateUserResponse', () => Promise.reject(err)); + + it(`fails with ${err}`, () => { + return expect($subject).to.be.rejectedWith(err); + }) + }); + + context('when UserManager#update() succeeds', () => { + it('calls EmailActivationCodeService#deleteActivationCode() with correct args', async () => { + await $subject; + expect(EmailActivationCodeService.deleteActivationCode).to.have.been.calledWith(codeData.activationCode, + transaction); + }); + + context('when EmailActivationCodeService#deleteActivationCode() fails', () => { + def('deleteActivationCodeResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when EmailActivationCodeService#deleteActivationCode() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }) + }) + }); + + describe('.logout()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + def('subject', () => $subject.logout(user, isCLI, transaction)); + def('removeAccessTokenResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AccessTokenService, 'removeAccessTokenByUserId').returns($removeAccessTokenResponse); + }); + + it('calls AccessTokenService#removeAccessTokenByUserId() with correct args', async () => { + await $subject; + expect(AccessTokenService.removeAccessTokenByUserId).to.have.been.calledWith(user.id, transaction); + }); + + context('when AccessTokenService#removeAccessTokenByUserId() fails', () => { + def('removeAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when AccessTokenService#removeAccessTokenByUserId() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }); + + describe('.updateUserDetails()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const profileData = { + firstName: 'testFirstName', + lastName: 'testLastName' + }; + + def('subject', () => $subject.updateUserDetails(user, profileData, isCLI, transaction)); + def('validatorResponse', () => Promise.resolve(true)); + def('deleteUndefinedFieldsResponse', () => profileData); + def('updateDetailsResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(Validator, 'validate').returns($validatorResponse); + $sandbox.stub(AppHelper, 'deleteUndefinedFields').returns($deleteUndefinedFieldsResponse); + $sandbox.stub(UserManager, 'updateDetails').returns($updateDetailsResponse); + }); + + it('calls Validator#validate() with correct args', async () => { + await $subject; + expect(Validator.validate).to.have.been.calledWith(profileData, Validator.schemas.updateUserProfile); + }); + + context('when Validator#validate() fails', () => { + def('validatorResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when Validator#validate() succeeds', () => { + it('calls AppHelper#deleteUndefinedFields() with correct args', async () => { + await $subject; + expect(AppHelper.deleteUndefinedFields).to.have.been.calledWith(profileData); + }); + + context('when AppHelper#deleteUndefinedFields() fails', () => { + def('deleteUndefinedFieldsResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('firstName'); + }) + }); + + context('when AppHelper#deleteUndefinedFields() succeeds', () => { + it('calls UserManager#updateDetails() with correct args', async () => { + await $subject; + expect(UserManager.updateDetails).to.have.been.calledWith(user, profileData, transaction); + }); + + context('when UserManager#updateDetails() fails', () => { + def('updateUserResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.have.property('firstName'); + }) + }); + + context('when UserManager#updateDetails() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.have.property('firstName'); + }) + }) + }) + }) + }); + + describe('.deleteUser()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + email: 'testEmail', + password: 'testPassword', + id: 15 + }; + + const profileData = { + firstName: 'testFirstName', + lastName: 'testLastName' + }; + + const force = false; + + def('subject', () => $subject.deleteUser(force, user, isCLI, transaction)); + def('findAllResponse', () => Promise.resolve([{}])); + def('deleteUserResponse', () => profileData); + + beforeEach(() => { + $sandbox.stub(ioFogManager, 'findAll').returns($findAllResponse); + $sandbox.stub(UserManager, 'delete').returns($deleteUserResponse); + }); + + it('calls ioFogManager#findAll() with correct args', async () => { + await $subject; + expect(ioFogManager.findAll).to.have.been.calledWith({ + userId: user.id + }, transaction); + }); + + context('when ioFogManager#findAll() fails', () => { + def('findAllResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when ioFogManager#findAll() succeeds', () => { + it('calls UserManager#delete() with correct args', async () => { + await $subject; + expect(UserManager.delete).to.have.been.calledWith({ + id: user.id + }, transaction); + }); + + context('when UserManager#delete() fails', () => { + def('deleteUserResponse', () => error); + + it(`fails with ${error}`, () => { + return expect($subject).to.eventually.equal(undefined); + }) + }); + + context('when UserManager#delete() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }); + + // TODO updateUserPassword, resetUserPassword with rewire + + describe('.list()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const response = [{ + id: user.id + }]; + + def('subject', () => $subject.list(isCLI, transaction)); + def('findAllResponse', () => Promise.resolve(response)); + + beforeEach(() => { + $sandbox.stub(UserManager, 'findAll').returns($findAllResponse); + }); + + it('calls UserManager#findAll() with correct args', async () => { + await $subject; + expect(UserManager.findAll).to.have.been.calledWith({}, transaction); + }); + + context('when UserManager#findAll() fails', () => { + def('findAllResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#findAll() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(response); + }) + }) + }); + + describe('.suspendUser()', () => { + const transaction = {}; + const error = 'Error!'; + + const user = { + id: 15 + }; + + const updatedObj = { + emailActivated: false + }; + + def('subject', () => $subject.suspendUser(user, isCLI, transaction)); + def('removeAccessTokenResponse', () => Promise.resolve()); + def('updateUserResponse', () => Promise.resolve()); + + beforeEach(() => { + $sandbox.stub(AccessTokenService, 'removeAccessTokenByUserId').returns($removeAccessTokenResponse); + $sandbox.stub(UserManager, 'update').returns($updateUserResponse); + }); + + it('calls AccessTokenService#removeAccessTokenByUserId() with correct args', async () => { + await $subject; + expect(AccessTokenService.removeAccessTokenByUserId).to.have.been.calledWith(user.id, transaction); + }); + + context('when AccessTokenService#removeAccessTokenByUserId() fails', () => { + def('removeAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when AccessTokenService#removeAccessTokenByUserId() succeeds', () => { + it('calls UserManager#update() with correct args', async () => { + await $subject; + expect(UserManager.update).to.have.been.calledWith({ + id: user.id + }, updatedObj, transaction); + }); + + context('when UserManager#update() fails', () => { + def('removeAccessTokenResponse', () => Promise.reject(error)); + + it(`fails with ${error}`, () => { + return expect($subject).to.be.rejectedWith(error); + }) + }); + + context('when UserManager#update() succeeds', () => { + it('fulfills the promise', () => { + return expect($subject).to.eventually.equal(undefined); + }) + }) + }) + }); + +}); \ No newline at end of file