<commit_before>/*
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-manager
 *
 * iotagent-manager is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-manager is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-manager.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::daniel.moranjimenez@telefonica.com
 */

'use strict';

/*jshint camelcase:false */

var protocols = require('./protocolData'),
    request = require('request'),
    logger = require('logops'),
    errors = require('../errors'),
    _ = require('underscore'),
    async = require('async'),
    context = {
        op: 'IoTAManager.Redirector'
    };

function guessCollection(body) {
    if (body.services) {
        return 'services';
    } else if (body.devices) {
        return 'devices';
    } else {
        return null;
    }
}

function extractProtocols(body) {
    var collectionName = guessCollection(body),
        protocols;

    logger.debug(context, 'Extracting protocols from body for collection [%s]', collectionName);

    function extractProtocol(previous, item) {
        if (item.protocol) {
            if (Array.isArray(item.protocol)) {
                return _.union(previous, item.protocol);
            } else {
                previous.push(item.protocol);

                return _.uniq(previous);
            }
        } else {
            return previous;
        }
    }

    if (collectionName) {
        protocols = body[collectionName].reduce(extractProtocol, []);

        logger.debug(context, 'Protocols found: %j', protocols);

        return protocols;
    } else {
        return null;
    }
}

function queryParamExtractor(req, res, next) {
    if (req.query.protocol) {
        logger.debug(context, 'Protocol found in the query parameters [%s]', req.query.protocol);
        req.protocolId = [req.query.protocol];
    } else if (req.method !== 'GET') {
        req.splitAndRedirect = true;
        req.protocolId = extractProtocols(req.body);
    }

    next();
}

function getProtocols(req, res, next) {
    function addProtocolAsync(previous, item, callback) {
        protocols.get(item, function(error, protocol) {
            if (error) {
                callback(error);
            } else {
                previous[item] = protocol;
                callback(null, previous);
            }
        });
    }

    function addProtocolSync(previous, item) {
        previous[item.protocol] = item;
        return previous;
    }

    function extractId(protocol) {
        return protocol.protocol;
    }

    if (req.protocolId) {
        logger.debug(context, 'Finding objects for the following protocols: %j', req.protocolId);

        async.reduce(req.protocolId, {}, addProtocolAsync, function endReduction(error, result) {
            req.protocolObj = result;
            next(error);
        });
    } else {
        logger.debug(context, 'Finding objects for all the protocols');

        protocols.list(function(error, protocols) {
            if (error) {
                next(error);
            } else {
                req.protocolId = protocols.map(extractId);
                req.protocolObj = protocols.reduce(addProtocolSync, {});
                logger.debug(context, 'Finding objects for this protocol: %j', req.protocolId);
                next();
            }
        });
    }
}

function splitByProtocol(req) {
    var collectionName = guessCollection(req.body),
        collections = {},
        clonedObject;

    for (var i = 0; i < req.body[collectionName].length; i++) {
        if (Array.isArray(req.body[collectionName][i].protocol)) {
            for (var j = 0; j < req.body[collectionName][i].protocol.length; j++) {
                clonedObject = _.clone(req.body[collectionName][i]);

                clonedObject.protocol = req.body[collectionName][i].protocol[j];
                clonedObject.resource = req.protocolObj[clonedObject.protocol].resource;

                if (collections[req.body[collectionName][i].protocol[j]]) {
                    collections[req.body[collectionName][i].protocol[j]][collectionName].push(clonedObject);
                } else {
                    collections[req.body[collectionName][i].protocol[j]] = {};
                    collections[req.body[collectionName][i].protocol[j]][collectionName] = [clonedObject];
                }
            }
        } else {
            clonedObject = _.clone(req.body[collectionName][i]);

            clonedObject.protocol = req.body[collectionName][i].protocol;
            clonedObject.resource = req.protocolObj[clonedObject.protocol].resource;

            collections[req.body[collectionName][i].protocol] = {};
            collections[req.body[collectionName][i].protocol][collectionName] = [_.clone(req.body[collectionName][i])];
        }
    }

    return collections;
}

/**
 * Create the redirected request based on the protocol information and the original request. For backwards compatibility
 * the IoT Agent Manager removes the "/iot" prefix from the protocol address if found.
 *
 * @param {Object} req           Original request object to be forwarded.
 * @param {String} protocol      ID of the protocl where the request will be forwarded
 * @param {Object} body          JSON body of the original request.
 * @return {Object}             Request object prepared to be sent.
 */
function createRequest(req, protocol, body) {
    var options = {
            qs: _.clone(req.query),
            method: req.method,
            headers: req.headers
        },
        protocolObj = req.protocolObj[protocol],
        protocolAddress = protocolObj.iotagent;

    logger.debug(context, 'creating request using protocol:\n\n%j\n', protocolObj);
    delete options.qs.protocol;

    // Save original apikey before be overwrite by apikey from body (probably modified)
    if (req.query.apikey) {
        options.qs.apikeyReq = req.query.apikey;
    }
    if (protocolObj.resource) {
        options.qs.resource = protocolObj.resource;
    }
    if (protocolAddress.substr(-1) === '/') {
        protocolAddress = protocolAddress.slice(0, -1);
    }

    if (protocolAddress.substr(-4) === '/iot') {
        protocolAddress = protocolAddress.slice(0, -4);
    }

    options.uri = protocolAddress + req.path;

    if (body && body.services) {
        body.services = body.services.map(function cleanProtocol(item) {
            delete item.protocol;
            return item;
        });
        // Translate body.services

        if (req.method === 'PUT') {
            var services = body.services.map(function translate(item) {
                return item;
            });
            body = services[0];
        }
    }

    if (req.method === 'PUT' || req.method === 'POST') {
        options.body = JSON.stringify(body);

    }

    delete options.headers['content-length'];
    options.headers.connection = 'close';

    logger.debug(context, 'Forwarding request:\n\n%j\n', options);

    return options;
}

function createRequests(req, res, next) {
    function mapToBody(split, protocol) {
        return createRequest(req, protocol, split[protocol]);
    }

    if (req.splitAndRedirect) {
        var split = splitByProtocol(req);

        logger.debug(context, 'Creating request for multiple elements');

        req.requests = req.protocolId.map(mapToBody.bind(null, split));
    } else {
        logger.debug(context, 'Creating request for redirecting single requests');
        req.requests = [];
        for (var i = 0; i < req.protocolId.length; i++) {
            req.requests.push(createRequest(req, req.protocolId[i], req.body));
        }
    }

    next();
}

function processRequests(req, res, next) {

    function extractStatusCode(result) {
        if (result && result.length === 2 && result[0].statusCode) {
            return result[0].statusCode;
        } else {
            return 0;
        }
    }

    function sendRequest(options, callback) {
        logger.debug(context, 'Sending redirection with following options: %j', options);
        // Use original apikey, not apikey from body
        if (options.qs.apikeyReq) {
            options.qs.apikey = options.qs.apikeyReq;
        }
        request(options, function(error, response, body) {
            if (error) {
                logger.error(context, 'Error found redirecting requests: %j', error);

                callback(new errors.TargetServerError(error));
            } else if (response.statusCode !== 200 &&
                       response.statusCode !== 201 &&
                       response.statusCode !== 204 &&
                       response.statusCode !== 409) {
                logger.error(context, 'Wrong status code detected [%s] and body response [%s] redirecting request',
                             response.statusCode,
                             response.body);
                callback(new errors.TargetServerError(
                    'Wrong status code detected [' + response.statusCode + '] and body response [' + response.body +
                        '] redirecting request'));
            } else if (body && body.length > 0) {
                var parsedBody,
                    parseError;

                try {
                    parsedBody = JSON.parse(body);
                    logger.debug(context, 'Response body from the redirected request parsed: %j', parsedBody);
                } catch (e) {
                    logger.error(context, 'Error parsing response body from the redirected request: %s', e);
                    parseError = new errors.TargetResponseError(e);
                }

                callback(parseError, [response, parsedBody]);
            } else {
                callback(null, [response, null]);
            }
        });
    }

    function combineResults(results) {
        /* jshint camelcase: false */

        var totalCount = 0,
            finalResult = [],
            collectionName,
            resultObj = {};

        if (results[0] && results[0][1]) {
            if (results[0][1].services || results[0][1].apikey) {
                collectionName = 'services';
            } else if (results[0][1].devices || results[0][1].device_id) {
                collectionName = 'devices';
            } else {
                return null;
            }
        } else {
            return null;
        }

        for (var i = 0; i < results.length; i++) {
            if (results[i][1].count) {
                totalCount += results[i][1].count;
            } else {
                totalCount++;
            }

            if (results[i][1][collectionName]) {
                finalResult = finalResult.concat(results[i][1][collectionName]);
            } else {
                finalResult = finalResult.concat(results[i][1]);
            }
        }

        resultObj.count = totalCount;
        resultObj[collectionName] = finalResult;

        return resultObj;
    }

    function requestHandler(error, results) {
        var combinedResult;

        if (error) {
            logger.error(context, 'The redirection ended up in error: ', error);
            next(error);
        } else {
            logger.debug(context, 'results %j', results);
            var statusCodes = _.uniq(results.map(extractStatusCode));

            if (statusCodes.length === 1) {
                if (req.method === 'POST' || req.method === 'DELETE' || req.method === 'PUT') {
                    res.status(statusCodes[0]).send();
                } else if (results.length >= 1) {
                    combinedResult = combineResults(results);

                    if (combinedResult) {
                        res.status(statusCodes[0]).json(combinedResult);
                    } else {
                        next(new errors.TargetServerError('No valid replies from any IoTAgent'));
                    }
                } else {
                    res.status(statusCodes[0]).json({});
                }
            } else {
                var errorMsg = 'Wrong status code obtained in some of the responses [' +
                    JSON.stringify(statusCodes) + '] redirecting request';

                logger.error(context, errorMsg);

                next(new errors.TargetServerError(errorMsg));
            }
        }
    }

    async.map(req.requests, sendRequest, requestHandler);
}

function loadContextRoutes(router) {
    var middlewareList = [
        queryParamExtractor,
        getProtocols,
        createRequests,
        processRequests
    ];

    router.post('/iot/services', middlewareList);
    router.post('/iot/devices', middlewareList);
    router.put('/iot/services', middlewareList);
    router.delete('/iot/services', middlewareList);
    router.get('/iot/devices', middlewareList);
    router.get('/iot/devices/:id', middlewareList);
    router.put('/iot/devices/:id', middlewareList);
    router.delete('/iot/devices/:id', middlewareList);

}

exports.loadContextRoutes = loadContextRoutes;
<commit_msg>remove unnecessary map function<commit_after>/*
 * Copyright 2016 Telefonica Investigación y Desarrollo, S.A.U
 *
 * This file is part of iotagent-manager
 *
 * iotagent-manager is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the License,
 * or (at your option) any later version.
 *
 * iotagent-manager is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public
 * License along with iotagent-manager.
 * If not, seehttp://www.gnu.org/licenses/.
 *
 * For those usages not covered by the GNU Affero General Public License
 * please contact with::daniel.moranjimenez@telefonica.com
 */

'use strict';

/*jshint camelcase:false */

var protocols = require('./protocolData'),
    request = require('request'),
    logger = require('logops'),
    errors = require('../errors'),
    _ = require('underscore'),
    async = require('async'),
    context = {
        op: 'IoTAManager.Redirector'
    };

function guessCollection(body) {
    if (body.services) {
        return 'services';
    } else if (body.devices) {
        return 'devices';
    } else {
        return null;
    }
}

function extractProtocols(body) {
    var collectionName = guessCollection(body),
        protocols;

    logger.debug(context, 'Extracting protocols from body for collection [%s]', collectionName);

    function extractProtocol(previous, item) {
        if (item.protocol) {
            if (Array.isArray(item.protocol)) {
                return _.union(previous, item.protocol);
            } else {
                previous.push(item.protocol);

                return _.uniq(previous);
            }
        } else {
            return previous;
        }
    }

    if (collectionName) {
        protocols = body[collectionName].reduce(extractProtocol, []);

        logger.debug(context, 'Protocols found: %j', protocols);

        return protocols;
    } else {
        return null;
    }
}

function queryParamExtractor(req, res, next) {
    if (req.query.protocol) {
        logger.debug(context, 'Protocol found in the query parameters [%s]', req.query.protocol);
        req.protocolId = [req.query.protocol];
    } else if (req.method !== 'GET') {
        req.splitAndRedirect = true;
        req.protocolId = extractProtocols(req.body);
    }

    next();
}

function getProtocols(req, res, next) {
    function addProtocolAsync(previous, item, callback) {
        protocols.get(item, function(error, protocol) {
            if (error) {
                callback(error);
            } else {
                previous[item] = protocol;
                callback(null, previous);
            }
        });
    }

    function addProtocolSync(previous, item) {
        previous[item.protocol] = item;
        return previous;
    }

    function extractId(protocol) {
        return protocol.protocol;
    }

    if (req.protocolId) {
        logger.debug(context, 'Finding objects for the following protocols: %j', req.protocolId);

        async.reduce(req.protocolId, {}, addProtocolAsync, function endReduction(error, result) {
            req.protocolObj = result;
            next(error);
        });
    } else {
        logger.debug(context, 'Finding objects for all the protocols');

        protocols.list(function(error, protocols) {
            if (error) {
                next(error);
            } else {
                req.protocolId = protocols.map(extractId);
                req.protocolObj = protocols.reduce(addProtocolSync, {});
                logger.debug(context, 'Finding objects for this protocol: %j', req.protocolId);
                next();
            }
        });
    }
}

function splitByProtocol(req) {
    var collectionName = guessCollection(req.body),
        collections = {},
        clonedObject;

    for (var i = 0; i < req.body[collectionName].length; i++) {
        if (Array.isArray(req.body[collectionName][i].protocol)) {
            for (var j = 0; j < req.body[collectionName][i].protocol.length; j++) {
                clonedObject = _.clone(req.body[collectionName][i]);

                clonedObject.protocol = req.body[collectionName][i].protocol[j];
                clonedObject.resource = req.protocolObj[clonedObject.protocol].resource;

                if (collections[req.body[collectionName][i].protocol[j]]) {
                    collections[req.body[collectionName][i].protocol[j]][collectionName].push(clonedObject);
                } else {
                    collections[req.body[collectionName][i].protocol[j]] = {};
                    collections[req.body[collectionName][i].protocol[j]][collectionName] = [clonedObject];
                }
            }
        } else {
            clonedObject = _.clone(req.body[collectionName][i]);

            clonedObject.protocol = req.body[collectionName][i].protocol;
            clonedObject.resource = req.protocolObj[clonedObject.protocol].resource;

            collections[req.body[collectionName][i].protocol] = {};
            collections[req.body[collectionName][i].protocol][collectionName] = [_.clone(req.body[collectionName][i])];
        }
    }

    return collections;
}

/**
 * Create the redirected request based on the protocol information and the original request. For backwards compatibility
 * the IoT Agent Manager removes the "/iot" prefix from the protocol address if found.
 *
 * @param {Object} req           Original request object to be forwarded.
 * @param {String} protocol      ID of the protocl where the request will be forwarded
 * @param {Object} body          JSON body of the original request.
 * @return {Object}             Request object prepared to be sent.
 */
function createRequest(req, protocol, body) {
    var options = {
            qs: _.clone(req.query),
            method: req.method,
            headers: req.headers
        },
        protocolObj = req.protocolObj[protocol],
        protocolAddress = protocolObj.iotagent;

    logger.debug(context, 'creating request using protocol:\n\n%j\n', protocolObj);
    delete options.qs.protocol;

    // Save original apikey before be overwrite by apikey from body (probably modified)
    if (req.query.apikey) {
        options.qs.apikeyReq = req.query.apikey;
    }
    if (protocolObj.resource) {
        options.qs.resource = protocolObj.resource;
    }
    if (protocolAddress.substr(-1) === '/') {
        protocolAddress = protocolAddress.slice(0, -1);
    }

    if (protocolAddress.substr(-4) === '/iot') {
        protocolAddress = protocolAddress.slice(0, -4);
    }

    options.uri = protocolAddress + req.path;

    if (body && body.services) {
        body.services = body.services.map(function cleanProtocol(item) {
            delete item.protocol;
            return item;
        });
        // Translate body.services

        if (req.method === 'PUT') {
            body = services[0];
        }
    }

    if (req.method === 'PUT' || req.method === 'POST') {
        options.body = JSON.stringify(body);

    }

    delete options.headers['content-length'];
    options.headers.connection = 'close';

    logger.debug(context, 'Forwarding request:\n\n%j\n', options);

    return options;
}

function createRequests(req, res, next) {
    function mapToBody(split, protocol) {
        return createRequest(req, protocol, split[protocol]);
    }

    if (req.splitAndRedirect) {
        var split = splitByProtocol(req);

        logger.debug(context, 'Creating request for multiple elements');

        req.requests = req.protocolId.map(mapToBody.bind(null, split));
    } else {
        logger.debug(context, 'Creating request for redirecting single requests');
        req.requests = [];
        for (var i = 0; i < req.protocolId.length; i++) {
            req.requests.push(createRequest(req, req.protocolId[i], req.body));
        }
    }

    next();
}

function processRequests(req, res, next) {

    function extractStatusCode(result) {
        if (result && result.length === 2 && result[0].statusCode) {
            return result[0].statusCode;
        } else {
            return 0;
        }
    }

    function sendRequest(options, callback) {
        logger.debug(context, 'Sending redirection with following options: %j', options);
        // Use original apikey, not apikey from body
        if (options.qs.apikeyReq) {
            options.qs.apikey = options.qs.apikeyReq;
        }
        request(options, function(error, response, body) {
            if (error) {
                logger.error(context, 'Error found redirecting requests: %j', error);

                callback(new errors.TargetServerError(error));
            } else if (response.statusCode !== 200 &&
                       response.statusCode !== 201 &&
                       response.statusCode !== 204 &&
                       response.statusCode !== 409) {
                logger.error(context, 'Wrong status code detected [%s] and body response [%s] redirecting request',
                             response.statusCode,
                             response.body);
                callback(new errors.TargetServerError(
                    'Wrong status code detected [' + response.statusCode + '] and body response [' + response.body +
                        '] redirecting request'));
            } else if (body && body.length > 0) {
                var parsedBody,
                    parseError;

                try {
                    parsedBody = JSON.parse(body);
                    logger.debug(context, 'Response body from the redirected request parsed: %j', parsedBody);
                } catch (e) {
                    logger.error(context, 'Error parsing response body from the redirected request: %s', e);
                    parseError = new errors.TargetResponseError(e);
                }

                callback(parseError, [response, parsedBody]);
            } else {
                callback(null, [response, null]);
            }
        });
    }

    function combineResults(results) {
        /* jshint camelcase: false */

        var totalCount = 0,
            finalResult = [],
            collectionName,
            resultObj = {};

        if (results[0] && results[0][1]) {
            if (results[0][1].services || results[0][1].apikey) {
                collectionName = 'services';
            } else if (results[0][1].devices || results[0][1].device_id) {
                collectionName = 'devices';
            } else {
                return null;
            }
        } else {
            return null;
        }

        for (var i = 0; i < results.length; i++) {
            if (results[i][1].count) {
                totalCount += results[i][1].count;
            } else {
                totalCount++;
            }

            if (results[i][1][collectionName]) {
                finalResult = finalResult.concat(results[i][1][collectionName]);
            } else {
                finalResult = finalResult.concat(results[i][1]);
            }
        }

        resultObj.count = totalCount;
        resultObj[collectionName] = finalResult;

        return resultObj;
    }

    function requestHandler(error, results) {
        var combinedResult;

        if (error) {
            logger.error(context, 'The redirection ended up in error: ', error);
            next(error);
        } else {
            logger.debug(context, 'results %j', results);
            var statusCodes = _.uniq(results.map(extractStatusCode));

            if (statusCodes.length === 1) {
                if (req.method === 'POST' || req.method === 'DELETE' || req.method === 'PUT') {
                    res.status(statusCodes[0]).send();
                } else if (results.length >= 1) {
                    combinedResult = combineResults(results);

                    if (combinedResult) {
                        res.status(statusCodes[0]).json(combinedResult);
                    } else {
                        next(new errors.TargetServerError('No valid replies from any IoTAgent'));
                    }
                } else {
                    res.status(statusCodes[0]).json({});
                }
            } else {
                var errorMsg = 'Wrong status code obtained in some of the responses [' +
                    JSON.stringify(statusCodes) + '] redirecting request';

                logger.error(context, errorMsg);

                next(new errors.TargetServerError(errorMsg));
            }
        }
    }

    async.map(req.requests, sendRequest, requestHandler);
}

function loadContextRoutes(router) {
    var middlewareList = [
        queryParamExtractor,
        getProtocols,
        createRequests,
        processRequests
    ];

    router.post('/iot/services', middlewareList);
    router.post('/iot/devices', middlewareList);
    router.put('/iot/services', middlewareList);
    router.delete('/iot/services', middlewareList);
    router.get('/iot/devices', middlewareList);
    router.get('/iot/devices/:id', middlewareList);
    router.put('/iot/devices/:id', middlewareList);
    router.delete('/iot/devices/:id', middlewareList);

}

exports.loadContextRoutes = loadContextRoutes;
<|endoftext|>


<commit_before>// console.log('package-post-init start')
Dapple.init = function init(env) {
  if (env === 'test' || env === 'ropsten') {
    Dapple.env = 'ropsten';
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.ropsten);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.ropsten);
    Dapple.makerjs = new Dapple.Maker(web3, 'ropsten');
  } else if (env === 'live' || env === 'main') {
    Dapple.env = 'live';
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.live);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.live);
    Dapple.makerjs = new Dapple.Maker(web3, 'live');
  } else if (env === 'private' || env === 'default') {
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.default);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.default);
  } else if (env === 'morden') {
    Dapple.env = 'morden';
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.morden);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.morden);
    Dapple.makerjs = new Dapple.Maker(web3, 'morden');
  }

  if (env !== false) {
    // Check if contract exists on new environment
    const contractAddress = Dapple['maker-otc'].environments[Dapple.env].otc.value;
    web3.eth.getCode(contractAddress, (error, code) => {
      Session.set('contractExists', !error && typeof code === 'string' && code !== '' && code !== '0x');
    });
  }
};

// XXX generated blocknumbers, should use incremental lookback instead
Dapple.getFirstContractBlock = () => {
  let blockNumber = 0;
  if (Dapple.env === 'live') {
    blockNumber = 2100636;
  } else if (Dapple.env === 'ropsten') {
    blockNumber = 23612;
  } else if (Dapple.env === 'morden') {
    blockNumber = 1524881;
  }
  return blockNumber;
};

const tokens = {
  ropsten: {
    'W-ETH': '0xece9fa304cc965b00afc186f5d0281a00d3dbbfd',
    DAI: '0x0000000000000000000000000000000000000000',
    MKR: '0xa7f6c9a5052a08a14ff0e3349094b6efbc591ea4',
    DGD: '0x1ab3bd2e2670d6e00ca406217e4d327f7f946d7e',
    GNT: '0x7fb3c4ff78bd0305a6ec436eda79303f981c5938',
    'W-GNT': '0xa5d92f318247c3b43241436dbb55ec4be600dc42',
    REP: '0x0000000000000000000000000000000000000000',
    ICN: '0x0000000000000000000000000000000000000000',
  },
  morden: {
    'W-ETH': '0x52fe88b987c7829e5d5a61c98f67c9c14e6a7a90',
    DAI: '0xa6581e37bb19afddd5c11f1d4e5fb16b359eb9fc',
    MKR: '0xffb1c99b389ba527a9194b1606b3565a07da3eef',
    DGD: '0x3c6f5633b30aa3817fa50b17e5bd30fb49bddd95',
    GNT: '0x0000000000000000000000000000000000000000',
    'W-GNT': '0x0000000000000000000000000000000000000000',
    REP: '0x0000000000000000000000000000000000000000',
    ICN: '0x0000000000000000000000000000000000000000',
  },
  live: {
    'W-ETH': '0xecf8f87f810ecf450940c9f60066b4a7a501d6a7',
    DAI: '0x0000000000000000000000000000000000000000',
    MKR: '0xc66ea802717bfb9833400264dd12c2bceaa34a6d',
    DGD: '0xe0b7927c4af23765cb51314a0e0521a9645f0e2a',
    GNT: '0xa74476443119a942de498590fe1f2454d7d4ac0d',
    'W-GNT': '0x01afc37f4f85babc47c0e2d0eababc7fb49793c8',
    REP: '0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5',
    ICN: '0x888666ca69e0f178ded6d75b5726cee99a87d698',
  },
};

// http://numeraljs.com/ for formats
const tokenSpecs = {
  'W-ETH': { precision: 18, format: '0,0.00[0000000000000000]' },
  DAI: { precision: 18, format: '0,0.00[0000000000000000]' },
  MKR: { precision: 18, format: '0,0.00[0000000000000000]' },
  DGD: { precision: 9, format: '0,0.00[0000000]' },
  GNT: { precision: 18, format: '0,0.00[0000000000000000]' },
  'W-GNT': { precision: 18, format: '0,0.00[0000000000000000]' },
  REP: { precision: 18, format: '0,0.00[0000000000000000]' },
  ICN: { precision: 18, format: '0,0.00[0000000000000000]' },
};

Dapple.getQuoteTokens = () => ['W-ETH'];

Dapple.getBaseTokens = () => ['MKR', 'DAI', 'DGD', 'W-GNT', 'REP', 'ICN'];

Dapple.getTokens = () => ['W-ETH', 'MKR', 'DAI', 'DGD', 'W-GNT', 'REP', 'ICN'];

Dapple.getTokenSpecs = (symbol) => {
  if (typeof (tokenSpecs[symbol]) !== 'undefined') {
    return tokenSpecs[symbol];
  }
  return tokenSpecs['W-ETH'];
};

Dapple.getTokenAddress = (symbol) => tokens[Dapple.env][symbol];

Dapple.getTokenByAddress = (address) => _.invert(tokens[Dapple.env])[address];

Dapple.getToken = (symbol, callback) => {
  if (!(Dapple.env in tokens)) {
    callback('Unknown environment', null);
    return;
  }
  if (!(symbol in tokens[Dapple.env])) {
    callback(`Unknown token "${symbol}"`, null);
    return;
  }

  let tokenClass = 'DSTokenFrontend';
  const address = Dapple.getTokenAddress(symbol);
  let that = Dapple.makerjs.dappsys;

  if (symbol === 'W-ETH') {
    tokenClass = 'DSEthToken';
  } else if (symbol === 'W-GNT') {
    tokenClass = 'TokenWrapper';
    that = Dapple['token-wrapper'];
  }

  try {
    that.classes[tokenClass].at(address, (error, token) => {
      if (!error) {
        token.abi = that.classes[tokenClass].abi;
        callback(false, token);
      } else {
        callback(error, token);
      }
    });
  } catch (e) {
    callback(e, null);
  }
};
<commit_msg>hiding DAI<commit_after>// console.log('package-post-init start')
Dapple.init = function init(env) {
  if (env === 'test' || env === 'ropsten') {
    Dapple.env = 'ropsten';
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.ropsten);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.ropsten);
    Dapple.makerjs = new Dapple.Maker(web3, 'ropsten');
  } else if (env === 'live' || env === 'main') {
    Dapple.env = 'live';
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.live);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.live);
    Dapple.makerjs = new Dapple.Maker(web3, 'live');
  } else if (env === 'private' || env === 'default') {
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.default);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.default);
  } else if (env === 'morden') {
    Dapple.env = 'morden';
    Dapple['maker-otc'].class(web3, Dapple['maker-otc'].environments.morden);
    Dapple['token-wrapper'].class(web3, Dapple['token-wrapper'].environments.morden);
    Dapple.makerjs = new Dapple.Maker(web3, 'morden');
  }

  if (env !== false) {
    // Check if contract exists on new environment
    const contractAddress = Dapple['maker-otc'].environments[Dapple.env].otc.value;
    web3.eth.getCode(contractAddress, (error, code) => {
      Session.set('contractExists', !error && typeof code === 'string' && code !== '' && code !== '0x');
    });
  }
};

// XXX generated blocknumbers, should use incremental lookback instead
Dapple.getFirstContractBlock = () => {
  let blockNumber = 0;
  if (Dapple.env === 'live') {
    blockNumber = 2100636;
  } else if (Dapple.env === 'ropsten') {
    blockNumber = 23612;
  } else if (Dapple.env === 'morden') {
    blockNumber = 1524881;
  }
  return blockNumber;
};

const tokens = {
  ropsten: {
    'W-ETH': '0xece9fa304cc965b00afc186f5d0281a00d3dbbfd',
    DAI: '0x0000000000000000000000000000000000000000',
    MKR: '0xa7f6c9a5052a08a14ff0e3349094b6efbc591ea4',
    DGD: '0x1ab3bd2e2670d6e00ca406217e4d327f7f946d7e',
    GNT: '0x7fb3c4ff78bd0305a6ec436eda79303f981c5938',
    'W-GNT': '0xa5d92f318247c3b43241436dbb55ec4be600dc42',
    REP: '0x0000000000000000000000000000000000000000',
    ICN: '0x0000000000000000000000000000000000000000',
  },
  morden: {
    'W-ETH': '0x52fe88b987c7829e5d5a61c98f67c9c14e6a7a90',
    DAI: '0xa6581e37bb19afddd5c11f1d4e5fb16b359eb9fc',
    MKR: '0xffb1c99b389ba527a9194b1606b3565a07da3eef',
    DGD: '0x3c6f5633b30aa3817fa50b17e5bd30fb49bddd95',
    GNT: '0x0000000000000000000000000000000000000000',
    'W-GNT': '0x0000000000000000000000000000000000000000',
    REP: '0x0000000000000000000000000000000000000000',
    ICN: '0x0000000000000000000000000000000000000000',
  },
  live: {
    'W-ETH': '0xecf8f87f810ecf450940c9f60066b4a7a501d6a7',
    DAI: '0x0000000000000000000000000000000000000000',
    MKR: '0xc66ea802717bfb9833400264dd12c2bceaa34a6d',
    DGD: '0xe0b7927c4af23765cb51314a0e0521a9645f0e2a',
    GNT: '0xa74476443119a942de498590fe1f2454d7d4ac0d',
    'W-GNT': '0x01afc37f4f85babc47c0e2d0eababc7fb49793c8',
    REP: '0x48c80f1f4d53d5951e5d5438b54cba84f29f32a5',
    ICN: '0x888666ca69e0f178ded6d75b5726cee99a87d698',
  },
};

// http://numeraljs.com/ for formats
const tokenSpecs = {
  'W-ETH': { precision: 18, format: '0,0.00[0000000000000000]' },
  DAI: { precision: 18, format: '0,0.00[0000000000000000]' },
  MKR: { precision: 18, format: '0,0.00[0000000000000000]' },
  DGD: { precision: 9, format: '0,0.00[0000000]' },
  GNT: { precision: 18, format: '0,0.00[0000000000000000]' },
  'W-GNT': { precision: 18, format: '0,0.00[0000000000000000]' },
  REP: { precision: 18, format: '0,0.00[0000000000000000]' },
  ICN: { precision: 18, format: '0,0.00[0000000000000000]' },
};

Dapple.getQuoteTokens = () => ['W-ETH'];

Dapple.getBaseTokens = () => ['MKR', /*'DAI', */'DGD', 'W-GNT', 'REP', 'ICN'];

Dapple.getTokens = () => ['W-ETH', 'MKR', /*'DAI', */'DGD', 'W-GNT', 'REP', 'ICN'];

Dapple.getTokenSpecs = (symbol) => {
  if (typeof (tokenSpecs[symbol]) !== 'undefined') {
    return tokenSpecs[symbol];
  }
  return tokenSpecs['W-ETH'];
};

Dapple.getTokenAddress = (symbol) => tokens[Dapple.env][symbol];

Dapple.getTokenByAddress = (address) => _.invert(tokens[Dapple.env])[address];

Dapple.getToken = (symbol, callback) => {
  if (!(Dapple.env in tokens)) {
    callback('Unknown environment', null);
    return;
  }
  if (!(symbol in tokens[Dapple.env])) {
    callback(`Unknown token "${symbol}"`, null);
    return;
  }

  let tokenClass = 'DSTokenFrontend';
  const address = Dapple.getTokenAddress(symbol);
  let that = Dapple.makerjs.dappsys;

  if (symbol === 'W-ETH') {
    tokenClass = 'DSEthToken';
  } else if (symbol === 'W-GNT') {
    tokenClass = 'TokenWrapper';
    that = Dapple['token-wrapper'];
  }

  try {
    that.classes[tokenClass].at(address, (error, token) => {
      if (!error) {
        token.abi = that.classes[tokenClass].abi;
        callback(false, token);
      } else {
        callback(error, token);
      }
    });
  } catch (e) {
    callback(e, null);
  }
};
<|endoftext|>


<commit_before>import React from 'react'
import PropTypes from 'prop-types'
import {graphql} from 'react-apollo'
import compose from 'util/compose'
import gql from 'graphql-tag'
import BaseLayout from 'client/components/layout/BaseLayout'
import {
    SimpleButton,
    SimpleDialog,
    Typography,
    TextField,
    Divider,
    DeleteIconButton,
    Chip,
    SimpleList,
    Row,
    Col
} from 'ui/admin'
import FileDrop from 'client/components/FileDrop'
import Util from 'client/util'
import config from 'gen/config'

const {BACKUP_URL} = config

class DbDumpContainer extends React.Component {

    constructor(props) {
        super(props)

        this.state = {
            creatingDump: false,
            creatingMediaDump: false,
            importingDbDump: false,
            importingMediaDump: false,
            importMediaDumpDialog: false,
            importDbDumpDialog: false
        }
    }

    createDump() {
        this.setState({creatingDump: true})
        this.props.createDbDump().then(e => {
            console.log(e)
            this.setState({creatingDump: false})
        })

    }

    createMediaDump() {
        this.setState({creatingMediaDump: true})
        this.props.createMediaDump().then(e => {
            console.log(e)
            this.setState({creatingMediaDump: false})
        })

    }

    importMediaDump() {
        this.setState({importMediaDumpDialog: true})
    }

    importDbDump() {
        this.setState({importDbDumpDialog: true})
    }

    handleConfirmMediaDialog() {
        this.setState({importMediaDumpDialog: false})
    }

    handleConfirmDbDialog() {
        this.setState({importDbDumpDialog: false})
    }

    handleMediaDumpUpload(e) {
        console.log(e)
    }

    download(content, filename, contentType)
    {
        if(!contentType) contentType = 'application/octet-stream';
        var a = document.createElement('a');
        var blob = new Blob([content], {'type':contentType});
        a.href = window.URL.createObjectURL(blob);
        a.download = filename;
        a.click();
    }

    render() {
        const {dbDumps, mediaDumps} = this.props
        const {creatingDump, creatingMediaDump, importingMediaDump, importMediaDumpDialog, importDbDumpDialog, importingDbDump} = this.state
        return (
            <BaseLayout>
                <Typography variant="h3" gutterBottom>Backups</Typography>

                <Row spacing={4}>
                    <Col md={6}>

                        <SimpleButton variant="contained" color="primary"
                                      showProgress={creatingDump}
                                      onClick={this.createDump.bind(this)}>{creatingDump ? 'Dump is beeing created' : 'Create new db dump'}</SimpleButton>

                        <SimpleButton color="secondary"
                                      disabled={importDbDumpDialog}
                                      showProgress={importingDbDump}
                                      onClick={this.importDbDump.bind(this)}>{importingDbDump ? 'Dump is beeing restored' : 'Restore db dump'}</SimpleButton>

                        {dbDumps && dbDumps.results && dbDumps.results.length > 0 &&
                        <SimpleList items={dbDumps.results.reduce((a, i) => {
                            a.push({
                                primary: i.name,
                                onClick: () => {

                                    const xhr = new XMLHttpRequest

                                    xhr.open("GET", BACKUP_URL + '/dbdumps/' + i.name)
                                    xhr.responseType = 'blob'

                                    xhr.addEventListener("load", () => {
                                        if( xhr.status === 200) {

                                            this.download(xhr.response, 'backup.gz', 'application/gzip')
                                        }else{

                                            alert(`Invalid status ${xhr.status}`)
                                        }
                                    }, false)

                                    xhr.setRequestHeader('Authorization', Util.getAuthToken())
                                    // xhr.overrideMimeType( "application/octet-stream; charset=x-user-defined;" )
                                    xhr.send(null)

                                },
                                secondary: Util.formattedDatetime(i.createdAt) + ' - ' + i.size
                            })
                            return a
                        }, [])}/>
                        }
                    </Col>

                    <Col md={6}>

                        <SimpleButton variant="contained" color="primary"
                                      disabled={importMediaDumpDialog}
                                      showProgress={creatingMediaDump}
                                      onClick={this.createMediaDump.bind(this)}>{creatingMediaDump ? 'Dump is beeing created' : 'Create new media dump'}</SimpleButton>

                        <SimpleButton color="secondary"
                                      showProgress={importingMediaDump}
                                      onClick={this.importMediaDump.bind(this)}>{importingMediaDump ? 'Dump is beeing imported' : 'Import media dump'}</SimpleButton>

                        {mediaDumps && mediaDumps.results && mediaDumps.results.length > 0 &&
                        <SimpleList items={mediaDumps.results.reduce((a, i) => {
                            a.push({
                                primary: i.name,
                                onClick: () => {
                                    window.location.href = BACKUP_URL + '/mediadumbs/' + i.name
                                    //history.push(ADMIN_BASE_URL + '/post/' + post._id)
                                },
                                secondary: Util.formattedDatetime(i.createdAt) + ' - ' + i.size
                            })
                            return a
                        }, [])}/>
                        }
                    </Col>
                </Row>

                <SimpleDialog open={importMediaDumpDialog} onClose={this.handleConfirmMediaDialog.bind(this)}
                              actions={[{key: 'close', label: 'Close'}]}
                              title="Select a media dump file">

                    <FileDrop style={{width: '15rem', height: '10rem'}} accept="application/x-gzip"
                              uploadTo="/graphql/upload/mediadump"
                              label="Drop file here"/>

                </SimpleDialog>

                <SimpleDialog open={importDbDumpDialog} onClose={this.handleConfirmDbDialog.bind(this)}
                              actions={[{key: 'close', label: 'Close'}]}
                              title="Select a db dump file">

                    <FileDrop style={{width: '15rem', height: '10rem'}} accept="application/x-gzip"
                              uploadTo="/graphql/upload/dbdump"
                              label="Drop file here"/>

                </SimpleDialog>

            </BaseLayout>
        )
    }
}

DbDumpContainer.propTypes = {
    /* apollo client props */
    dbDumps: PropTypes.object,
    createDbDump: PropTypes.func.isRequired,
    createMediaDump: PropTypes.func.isRequired,
    loading: PropTypes.bool,
    mediaDumps: PropTypes.object
}

const gqlQuery = gql`query{dbDumps{results{name createdAt size}}}`
const gqlUpdate = gql`mutation createDbDump($type:String){createDbDump(type:$type){name createdAt size}}`
const gqlQueryMedia = gql`query{mediaDumps{results{name createdAt size}}}`
const gqlUpdateMedia = gql`mutation createMediaDump($type:String){createMediaDump(type:$type){name createdAt size}}`

const DbDumpContainerWithGql = compose(
    graphql(gqlQuery, {
        options() {
            return {
                fetchPolicy: 'cache-and-network'
            }
        },
        props: ({data: {dbDumps}}) => ({
            dbDumps
        })
    }),
    graphql(gqlUpdate, {
        props: ({mutate}) => ({
            createDbDump: () => {
                return mutate({
                    variables: {type: 'full'},
                    update: (proxy, {data: {createDbDump}}) => {
                        // Read the data from our cache for this query.
                        const data = proxy.readQuery({query: gqlQuery})
                        // Add our note from the mutation to the end.
                        data.dbDumps.results.unshift(createDbDump)
                        // Write our data back to the cache.
                        proxy.writeQuery({query: gqlQuery, data})
                    }

                })
            }
        })
    }),
    graphql(gqlQueryMedia, {
        options() {
            return {
                fetchPolicy: 'cache-and-network'
            }
        },
        props: ({data: {mediaDumps}}) => ({
            mediaDumps
        })
    }),
    graphql(gqlUpdateMedia, {
        props: ({mutate}) => ({
            createMediaDump: () => {
                return mutate({
                    variables: {type: 'full'},
                    update: (proxy, {data: {createMediaDump}}) => {
                        // Read the data from our cache for this query.
                        const data = proxy.readQuery({query: gqlQueryMedia})
                        // Add our note from the mutation to the end.
                        data.mediaDumps.results.unshift(createMediaDump)
                        // Write our data back to the cache.
                        proxy.writeQuery({query: gqlQueryMedia, data})
                    }

                })
            }
        })
    })
)
(DbDumpContainer)

/**
 * Connect the component to
 * the Redux store.
 */
export default DbDumpContainerWithGql
<commit_msg>Small improvments<commit_after>import React from 'react'
import PropTypes from 'prop-types'
import {graphql} from 'react-apollo'
import compose from 'util/compose'
import gql from 'graphql-tag'
import BaseLayout from 'client/components/layout/BaseLayout'
import {
    SimpleButton,
    SimpleDialog,
    Typography,
    TextField,
    Divider,
    DeleteIconButton,
    Chip,
    SimpleList,
    Row,
    Col
} from 'ui/admin'
import FileDrop from 'client/components/FileDrop'
import Util from 'client/util'
import config from 'gen/config'

const {BACKUP_URL} = config

class DbDumpContainer extends React.Component {

    constructor(props) {
        super(props)

        this.state = {
            creatingDump: false,
            creatingMediaDump: false,
            importingDbDump: false,
            importingMediaDump: false,
            importMediaDumpDialog: false,
            importDbDumpDialog: false
        }
    }

    createDump() {
        this.setState({creatingDump: true})
        this.props.createDbDump().then(e => {
            console.log(e)
            this.setState({creatingDump: false})
        })

    }

    createMediaDump() {
        this.setState({creatingMediaDump: true})
        this.props.createMediaDump().then(e => {
            console.log(e)
            this.setState({creatingMediaDump: false})
        })

    }

    importMediaDump() {
        this.setState({importMediaDumpDialog: true})
    }

    importDbDump() {
        this.setState({importDbDumpDialog: true})
    }

    handleConfirmMediaDialog() {
        this.setState({importMediaDumpDialog: false})
    }

    handleConfirmDbDialog() {
        this.setState({importDbDumpDialog: false})
    }

    handleMediaDumpUpload(e) {
        console.log(e)
    }

    download(content, filename, contentType)
    {
        if(!contentType) contentType = 'application/octet-stream';
        var a = document.createElement('a');
        var blob = new Blob([content], {'type':contentType});
        a.href = window.URL.createObjectURL(blob);
        a.download = filename;
        a.click();
    }

    authorizedRequest(url, name){
        const xhr = new XMLHttpRequest

        xhr.open(url)
        xhr.responseType = 'blob'

        xhr.addEventListener("load", () => {
            if( xhr.status === 200) {

                this.download(xhr.response, name, 'application/gzip')
            }else{

                alert(`Invalid status ${xhr.status}`)
            }
        }, false)

        xhr.setRequestHeader('Authorization', Util.getAuthToken())
        // xhr.overrideMimeType( "application/octet-stream; charset=x-user-defined;" )
        xhr.send(null)
    }

    render() {
        const {dbDumps, mediaDumps} = this.props
        const {creatingDump, creatingMediaDump, importingMediaDump, importMediaDumpDialog, importDbDumpDialog, importingDbDump} = this.state
        return (
            <BaseLayout>
                <Typography variant="h3" gutterBottom>Backups</Typography>

                <Row spacing={4}>
                    <Col md={6}>

                        <SimpleButton variant="contained" color="primary"
                                      showProgress={creatingDump}
                                      onClick={this.createDump.bind(this)}>{creatingDump ? 'Dump is beeing created' : 'Create new db dump'}</SimpleButton>

                        <SimpleButton color="secondary"
                                      disabled={importDbDumpDialog}
                                      showProgress={importingDbDump}
                                      onClick={this.importDbDump.bind(this)}>{importingDbDump ? 'Dump is beeing restored' : 'Restore db dump'}</SimpleButton>

                        {dbDumps && dbDumps.results && dbDumps.results.length > 0 &&
                        <SimpleList items={dbDumps.results.reduce((a, i) => {
                            a.push({
                                primary: i.name,
                                onClick: () => {

                                    this.authorizedRequest("GET", BACKUP_URL + '/dbdumps/' + i.name, 'db.backup.gz' )

                                },
                                secondary: Util.formattedDatetime(i.createdAt) + ' - ' + i.size
                            })
                            return a
                        }, [])}/>
                        }
                    </Col>

                    <Col md={6}>

                        <SimpleButton variant="contained" color="primary"
                                      disabled={importMediaDumpDialog}
                                      showProgress={creatingMediaDump}
                                      onClick={this.createMediaDump.bind(this)}>{creatingMediaDump ? 'Dump is beeing created' : 'Create new media dump'}</SimpleButton>

                        <SimpleButton color="secondary"
                                      showProgress={importingMediaDump}
                                      onClick={this.importMediaDump.bind(this)}>{importingMediaDump ? 'Dump is beeing imported' : 'Import media dump'}</SimpleButton>

                        {mediaDumps && mediaDumps.results && mediaDumps.results.length > 0 &&
                        <SimpleList items={mediaDumps.results.reduce((a, i) => {
                            a.push({
                                primary: i.name,
                                onClick: () => {
                                    this.authorizedRequest("GET", BACKUP_URL + '/mediadumbs/' + i.name, 'medias.backup.gz' )
                                },
                                secondary: Util.formattedDatetime(i.createdAt) + ' - ' + i.size
                            })
                            return a
                        }, [])}/>
                        }
                    </Col>
                </Row>

                <SimpleDialog open={importMediaDumpDialog} onClose={this.handleConfirmMediaDialog.bind(this)}
                              actions={[{key: 'close', label: 'Close'}]}
                              title="Select a media dump file">

                    <FileDrop style={{width: '15rem', height: '10rem'}} accept="application/x-gzip"
                              uploadTo="/graphql/upload/mediadump"
                              label="Drop file here"/>

                </SimpleDialog>

                <SimpleDialog open={importDbDumpDialog} onClose={this.handleConfirmDbDialog.bind(this)}
                              actions={[{key: 'close', label: 'Close'}]}
                              title="Select a db dump file">

                    <FileDrop style={{width: '15rem', height: '10rem'}} accept="application/x-gzip"
                              uploadTo="/graphql/upload/dbdump"
                              label="Drop file here"/>

                </SimpleDialog>

            </BaseLayout>
        )
    }
}

DbDumpContainer.propTypes = {
    /* apollo client props */
    dbDumps: PropTypes.object,
    createDbDump: PropTypes.func.isRequired,
    createMediaDump: PropTypes.func.isRequired,
    loading: PropTypes.bool,
    mediaDumps: PropTypes.object
}

const gqlQuery = gql`query{dbDumps{results{name createdAt size}}}`
const gqlUpdate = gql`mutation createDbDump($type:String){createDbDump(type:$type){name createdAt size}}`
const gqlQueryMedia = gql`query{mediaDumps{results{name createdAt size}}}`
const gqlUpdateMedia = gql`mutation createMediaDump($type:String){createMediaDump(type:$type){name createdAt size}}`

const DbDumpContainerWithGql = compose(
    graphql(gqlQuery, {
        options() {
            return {
                fetchPolicy: 'cache-and-network'
            }
        },
        props: ({data: {dbDumps}}) => ({
            dbDumps
        })
    }),
    graphql(gqlUpdate, {
        props: ({mutate}) => ({
            createDbDump: () => {
                return mutate({
                    variables: {type: 'full'},
                    update: (proxy, {data: {createDbDump}}) => {
                        // Read the data from our cache for this query.
                        const data = proxy.readQuery({query: gqlQuery})
                        // Add our note from the mutation to the end.
                        data.dbDumps.results.unshift(createDbDump)
                        // Write our data back to the cache.
                        proxy.writeQuery({query: gqlQuery, data})
                    }

                })
            }
        })
    }),
    graphql(gqlQueryMedia, {
        options() {
            return {
                fetchPolicy: 'cache-and-network'
            }
        },
        props: ({data: {mediaDumps}}) => ({
            mediaDumps
        })
    }),
    graphql(gqlUpdateMedia, {
        props: ({mutate}) => ({
            createMediaDump: () => {
                return mutate({
                    variables: {type: 'full'},
                    update: (proxy, {data: {createMediaDump}}) => {
                        // Read the data from our cache for this query.
                        const data = proxy.readQuery({query: gqlQueryMedia})
                        // Add our note from the mutation to the end.
                        data.mediaDumps.results.unshift(createMediaDump)
                        // Write our data back to the cache.
                        proxy.writeQuery({query: gqlQueryMedia, data})
                    }

                })
            }
        })
    })
)
(DbDumpContainer)

/**
 * Connect the component to
 * the Redux store.
 */
export default DbDumpContainerWithGql
<|endoftext|>


<commit_before>/**
* [editlisteController.js]
*
*
*/

app.controller('editlistCtrl', ['$scope', 'httpService', 'InputValidationService', '$location', '$rootScope', '$routeParams', 'SendNotification',
                            function($scope, httpService, InputValidationService, $location, $rootScope, $routeParams, SendNotification)
{

    /*
    * Initialize the counter which will be incremented every time a row is added to the DOM.
    * The incrementation is done by the 'addrow' directive in 'directive.js'.
	* N_elements is used to display how many element there is and its also increment in 'directive.js'.
    */
 	$scope.count = 1;
 	$scope.N_elements = 1;
 	$scope.numberOfElement = $scope.N_elements;

    /* Display the list name to be edited. */
    $scope.nomListe = '';

    /* Control the display of formular used to add elements. */
    $scope.DisplayAddItemToListeAlert = false;

    /*
    * Display a different message depending on the bollean value of 'DisplayAddItemToListeAlert'.
    * [true] = 'All fields must ...'
    * [false] = 'Error Server ...'
    */
    $scope.displayFormatAlert = false;

    /* Count properties of an object. */
    function ObjectLength( object ) {
        var length = 0;
        for( var key in object ) {
            if( object.hasOwnProperty(key) ) {
                ++length;
            }
        }
        return length;
    };

    /* Contain objects to edit. */
    $scope.inputGroup = {
        "object_1" : {
            "id_item":"",
            "ListeNom": "",
            "ListeDescription": ""
        }
    };

    /* Contain the list ID used to send the http request. */
    var EditID = $routeParams.id;

    /* Download lists asynchronously. */
    function LoadData(id) {

        /* Display loading spinner. */
        $scope.loading = true;

        /* ----Send request to server---- */

        /* Build request */
         var request = {
                method: 'GET',
                url: 'editliste/' + id
            }

            /* Call httpService */
            httpService.http(request)
                .then(function(response) { /* -----Response Server----- */

                    if (!response.error)
                    {
                        /* Send notification via service. */
                        SendNotification.turnOn();

                        $scope.loading = false;
                        $scope.nomListe = response.data.nomListe;

                        /* Create N rows of elements.  */
                        AddRows(response.data.listes.length);

                        /* Add element to the created rows. */
                        AddElementToListe(response.data.listes);

                        /* Remove alert. */
                        $scope.alerteServeur = false;

                    }
                    else
                    {
                        SendNotification.turnOff();
                        $location.path('login/0');
                    }

                },
                function(error) { /* -----Error Server----- */

                    $scope.loading = false;
                    $scope.alerteServeur = true;
                });
    }

    /* Download elements of a list when page loads. */
    window.onload = LoadData(EditID);

    /*
    * Trigger the 'addrow' directive by simulating as many clicks as there
    * are properties in the 'Data' object in order to display the elements to edit.
    */
    function AddRows(N) {
        var id = 1;
        var rows = ObjectLength($scope.Data);

        for (i=0 ; i < (N) ; i++)
        {
            document.getElementById(id).click();
            id++;
        }
    };

    /* Add the downloaded element to $scope.inputGroup. */
    function AddElementToListe(elem) {
        var i = 1;

        for (var key in elem) {
            var object = "object_" + i;
            $scope.inputGroup[object] = {
                "id_item": elem[key].id_item,
                "ListeNom": elem[key].nom_item,
                "ListeDescription": elem[key].description_item
            };
            i++;
         }
    }

    /*
    * This will be populated if the data submited are corrects.
    * This JSON which will contain all elements that compose
    * a list will then be sent to the server.
    */
    var JsonToSend = {
        'data' : []
    };

    /* Check all data before submit them to server */
    $scope.UpdateList = function() {

        $scope.loading = true;
        var NewListeAlert = false;

        /*
          Remove the last property in '$scope.inputGroup' related to the last
          row from the edit list if it is empty.
          The last row with the + sign is empty by default, so we remove it if the user
          has not add any data to prevent an error message when updating.
        */
        var AllKeys = [];
        for (var key in $scope.inputGroup) {
            if (!$scope.inputGroup.hasOwnProperty(key)) continue;
            AllKeys.push(key);
        }

        var key = AllKeys.pop();
        var LastRowNameLength = $scope.inputGroup[key].ListeNom.length
        var LastRowDecriptionLength = $scope.inputGroup[key].ListeDescription.length;

        if (LastRowDecriptionLength == 0 && LastRowNameLength == 0)
        {
           delete $scope.inputGroup[key];
        }

        for (var key in $scope.inputGroup) {

            /* Break the iteraton if object has no property. */
             if (!$scope.inputGroup.hasOwnProperty(key)) continue;

                 /* Check the data format with the InputValidationService. */
                 var controList = ['ListeNom', 'ListeDescription'];
                 var valid = InputValidationService.Validation($scope.inputGroup[key], controList);

                /* If the format is incorrect then display an alert and stop the loop. */
                 if (!valid.ListeNom || !valid.ListeDescription)
                 {
                     $scope.DisplayAddItemToListeAlert = true;
                     $scope.displayFormatAlert = true;
                     NewListeAlert = true;
                     $scope.loading = false;
                 }
                 if (NewListeAlert) { break; }

         }

        /* Submit data to server if there are correct */
        if (!NewListeAlert)
        {

           /* Populate 'JsonToSend'. */
           for (var key in $scope.inputGroup) {

                if (!$scope.inputGroup.hasOwnProperty(key)) continue;

                    JsonToSend.data.push({
                        "id_item": $scope.inputGroup[key].id_item,
                        'ListeNom' : $scope.inputGroup[key].ListeNom,
                        'ListeDescription': $scope.inputGroup[key].ListeDescription
                    });
            }

            /* Build the request */
            var request = {
                method: 'POST',
                url: 'editliste/' + EditID,
                headers: {
                    'Content-Type': 'application/json'
                },
                data: {
                    "listes": JSON.stringify(JsonToSend)
                }
            }

            /* Call httpService */
            httpService.http(request)
                .then(function(response) { /* ----Reponse Server----- */

                    /* Redirect to account page. */
                    if (response.redirect && !response.error)
                    {
                        SendNotification.turnOn();
                        $location.path('/account');
                    }

                    /* Redirect user if he is not logged. */
                    else if (response.error)
                    {
                        SendNotification.turnOff();
                        $location.path('/login/0');
                    }

                    $scope.loading = false;
                },
                function(error) { /* ----Error Server----- */

                    $scope.DisplayAddItemToListeAlert = true;
                    $scope.displayFormatAlert = false;
                    $scope.loading = false;

                });

        }
    }

}]);
        <commit_msg>Delete editlisteController.js<commit_after><|endoftext|>


<commit_before>var th = require('js/typeahead_helper.js');

set_global('page_params', {realm_is_zephyr_mirror_realm: false});
set_global('templates', {});

add_dependencies({
    stream_data: 'js/stream_data.js',
    people: 'js/people.js',
    util: 'js/util.js',
    Handlebars: 'handlebars',
});

stream_data.create_streams([
    {name: 'Dev', subscribed: true, color: 'blue', stream_id: 1},
    {name: 'Linux', subscribed: true, color: 'red', stream_id: 2},
]);

(function test_sort_streams() {
    var popular = {num_items: function () {
        return 10;
    }};

    var unpopular = {num_items: function () {
        return 2;
    }};

    var test_streams = [
        {name: 'Dev', pin_to_top: false, subscribers: unpopular},
        {name: 'Docs', pin_to_top: false, subscribers: popular},
        {name: 'Derp', pin_to_top: false, subscribers: unpopular},
        {name: 'Denmark', pin_to_top: true, subscribers: popular},
        {name: 'dead', pin_to_top: false, subscribers: unpopular},
    ];

    global.stream_data.is_active = function (sub) {
        return sub.name !== 'dead';
    };

    test_streams = th.sort_streams(test_streams, 'd');
    assert.deepEqual(test_streams[0].name, "Denmark"); // Pinned streams first
    assert.deepEqual(test_streams[1].name, "Docs"); // Active streams next
    assert.deepEqual(test_streams[2].name, "Derp"); // Less subscribers
    assert.deepEqual(test_streams[3].name, "Dev"); // Alphabetically last
    assert.deepEqual(test_streams[4].name, "dead"); // Inactive streams last

    // Test sort streams with description
    test_streams = [
        {name: 'Dev', description: 'development help', subscribers: unpopular},
        {name: 'Docs', description: 'writing docs', subscribers: popular},
        {name: 'Derp', description: 'derping around', subscribers: unpopular},
        {name: 'Denmark', description: 'visiting Denmark', subscribers: popular},
        {name: 'dead', description: 'dead stream', subscribers: unpopular},
    ];
    test_streams = th.sort_streams(test_streams, 'wr');
    assert.deepEqual(test_streams[0].name, "Docs"); // Description match
    assert.deepEqual(test_streams[1].name, "Denmark"); // Popular stream
    assert.deepEqual(test_streams[2].name, "Derp"); // Less subscribers
    assert.deepEqual(test_streams[3].name, "Dev"); // Alphabetically last
    assert.deepEqual(test_streams[4].name, "dead"); // Inactive streams last
}());

(function test_sort_languages() {
    set_global('pygments_data', {langs:
        {python: 40, javscript: 50, php: 38, pascal: 29, perl: 22, css: 0},
    });

    var test_langs = ["pascal", "perl", "php", "python", "javascript"];
    test_langs = th.sort_languages(test_langs, "p");

    // Sort languages by matching first letter, and then by popularity
    assert.deepEqual(test_langs, ["python", "php", "pascal", "perl", "javascript"]);

    // Test if popularity between two languages are the same
    global.pygments_data.langs.php = 40;
    test_langs = ["pascal", "perl", "php", "python", "javascript"];
    test_langs = th.sort_languages(test_langs, "p");

    assert.deepEqual(test_langs, ["php", "python", "pascal", "perl", "javascript"]);
}());

var matches = [
    {
        email: "a_bot@zulip.com",
        full_name: "A zulip test bot",
        is_admin: false,
        is_bot: true,
        user_id: 1,
    }, {
        email: "a_user@zulip.org",
        full_name: "A zulip user",
        is_admin: false,
        is_bot: false,
        user_id: 2,
    }, {
        email: "b_user_1@zulip.net",
        full_name: "Bob 1",
        is_admin: false,
        is_bot: false,
        user_id: 3,
    }, {
        email: "b_user_2@zulip.net",
        full_name: "Bob 2",
        is_admin: true,
        is_bot: false,
        user_id: 4,
    }, {
        email: "b_bot@example.com",
        full_name: "B bot",
        is_admin: false,
        is_bot: true,
        user_id: 5,
    }, {
        email: "zman@test.net",
        full_name: "Zman",
        is_admin: false,
        is_bot: false,
        user_id: 6,
    },
];

_.each(matches, function (person) {
    global.people.add_in_realm(person);
});

(function test_sort_recipients() {
    function get_typeahead_result(query, current_stream) {
        var result = th.sort_recipients(
            global.people.get_realm_persons(),
            query,
            current_stream
        );
        return _.map(result, function (person) {
            return person.email;
        });
    }

    assert.deepEqual(get_typeahead_result("b", ""), [
        'b_user_1@zulip.net',
        'b_user_2@zulip.net',
        'b_bot@example.com',
        'a_user@zulip.org',
        'zman@test.net',
        'a_bot@zulip.com',
     ]);

    var subscriber_email = "b_user_2@zulip.net";
    stream_data.add_subscriber("Dev", people.get_user_id(subscriber_email));
    assert.deepEqual(get_typeahead_result("b", "Dev"), [
        subscriber_email,
        'b_user_1@zulip.net',
        'b_bot@example.com',
        'a_user@zulip.org',
        'zman@test.net',
        'a_bot@zulip.com',
    ]);

    // No match
    assert.deepEqual(get_typeahead_result("h", "Linux"), [
        'a_user@zulip.org',
        'b_user_1@zulip.net',
        'b_user_2@zulip.net',
        'zman@test.net',
        'a_bot@zulip.com',
        'b_bot@example.com',
    ]);

    // Test person email is "all" or "everyone"
    var person = {
        email: "all",
        full_name: "All",
        is_admin: false,
        is_bot: false,
        user_id: 42,
    };
    people.add_in_realm(person);

    assert.deepEqual(get_typeahead_result("a", "Linux"), [
        'all',
        'a_user@zulip.org',
        'a_bot@zulip.com',
        'b_user_1@zulip.net',
        'b_user_2@zulip.net',
        'zman@test.net',
        'b_bot@example.com',
    ]);
}());

(function test_highlight_with_escaping() {
    var item = "Denmark";
    var query = "Den";
    var expected = "Denmark";
    var result = th.highlight_with_escaping(query, item);
    assert.equal(result, expected);

    item = "w3IrD_naMe";
    query = "w3IrD_naMe";
    expected = "w3IrD_naMe";
    result = th.highlight_with_escaping(query, item);
    assert.equal(result, expected);

    item = "development help";
    query = "development h";
    expected = "development help";
    result = th.highlight_with_escaping(query, item);
    assert.equal(result, expected);
}());

(function test_render_person() {
    // Test render_person with regular person
    var rendered = false;
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, matches[1].full_name);
        assert.equal(args.secondary, matches[1].email);
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_person(matches[1]), 'typeahead-item-stub');
    assert(rendered);

    // Test render_person with special_item_text person
    var special_person = {
        email: "special@example.com",
        full_name: "Special person",
        is_admin: false,
        is_bot: false,
        user_id: 7,
        special_item_text: "special_text",
    };
    rendered = false;
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, special_person.special_item_text);
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_person(special_person), 'typeahead-item-stub');
    assert(rendered);
}());

(function test_render_stream() {
    // Test render_stream with short description
    var rendered = false;
    var stream = {
        description: 'This is a short description.',
        stream_id: 42,
        name: 'Short Description',
    };
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, stream.name);
        assert.equal(args.secondary, stream.description);
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_stream(stream), 'typeahead-item-stub');
    assert(rendered);

    // Test render_stream with long description
    rendered = false;
    stream = {
        description: 'This is a very very very very very long description.',
        stream_id: 43,
        name: 'Long Description',
    };
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, stream.name);
        var short_desc = stream.description.substring(0, 35);
        assert.equal(args.secondary, short_desc + "...");
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_stream(stream), 'typeahead-item-stub');
    assert(rendered);
}());

(function test_sort_emojis() {
    var emoji_list = [
        { emoji_name: '+1' },
        { emoji_name: 'thumbs_up' },
        { emoji_name: 'pig' },
        { emoji_name: 'thumbs_down' },
    ];
    assert.deepEqual(th.sort_emojis(emoji_list, 'thumbs'), [
        { emoji_name: 'thumbs_up' },
        { emoji_name: 'thumbs_down' },
        { emoji_name: '+1' },
        { emoji_name: 'pig' },
    ]);
}());
<commit_msg>node tests: Fully cover compare_by_pms() in typeahead_helper.js.<commit_after>var th = require('js/typeahead_helper.js');

set_global('page_params', {realm_is_zephyr_mirror_realm: false});
set_global('templates', {});

add_dependencies({
    stream_data: 'js/stream_data.js',
    people: 'js/people.js',
    util: 'js/util.js',
    Handlebars: 'handlebars',
});

stream_data.create_streams([
    {name: 'Dev', subscribed: true, color: 'blue', stream_id: 1},
    {name: 'Linux', subscribed: true, color: 'red', stream_id: 2},
]);

(function test_sort_streams() {
    var popular = {num_items: function () {
        return 10;
    }};

    var unpopular = {num_items: function () {
        return 2;
    }};

    var test_streams = [
        {name: 'Dev', pin_to_top: false, subscribers: unpopular},
        {name: 'Docs', pin_to_top: false, subscribers: popular},
        {name: 'Derp', pin_to_top: false, subscribers: unpopular},
        {name: 'Denmark', pin_to_top: true, subscribers: popular},
        {name: 'dead', pin_to_top: false, subscribers: unpopular},
    ];

    global.stream_data.is_active = function (sub) {
        return sub.name !== 'dead';
    };

    test_streams = th.sort_streams(test_streams, 'd');
    assert.deepEqual(test_streams[0].name, "Denmark"); // Pinned streams first
    assert.deepEqual(test_streams[1].name, "Docs"); // Active streams next
    assert.deepEqual(test_streams[2].name, "Derp"); // Less subscribers
    assert.deepEqual(test_streams[3].name, "Dev"); // Alphabetically last
    assert.deepEqual(test_streams[4].name, "dead"); // Inactive streams last

    // Test sort streams with description
    test_streams = [
        {name: 'Dev', description: 'development help', subscribers: unpopular},
        {name: 'Docs', description: 'writing docs', subscribers: popular},
        {name: 'Derp', description: 'derping around', subscribers: unpopular},
        {name: 'Denmark', description: 'visiting Denmark', subscribers: popular},
        {name: 'dead', description: 'dead stream', subscribers: unpopular},
    ];
    test_streams = th.sort_streams(test_streams, 'wr');
    assert.deepEqual(test_streams[0].name, "Docs"); // Description match
    assert.deepEqual(test_streams[1].name, "Denmark"); // Popular stream
    assert.deepEqual(test_streams[2].name, "Derp"); // Less subscribers
    assert.deepEqual(test_streams[3].name, "Dev"); // Alphabetically last
    assert.deepEqual(test_streams[4].name, "dead"); // Inactive streams last
}());

(function test_sort_languages() {
    set_global('pygments_data', {langs:
        {python: 40, javscript: 50, php: 38, pascal: 29, perl: 22, css: 0},
    });

    var test_langs = ["pascal", "perl", "php", "python", "javascript"];
    test_langs = th.sort_languages(test_langs, "p");

    // Sort languages by matching first letter, and then by popularity
    assert.deepEqual(test_langs, ["python", "php", "pascal", "perl", "javascript"]);

    // Test if popularity between two languages are the same
    global.pygments_data.langs.php = 40;
    test_langs = ["pascal", "perl", "php", "python", "javascript"];
    test_langs = th.sort_languages(test_langs, "p");

    assert.deepEqual(test_langs, ["php", "python", "pascal", "perl", "javascript"]);
}());

var matches = [
    {
        email: "a_bot@zulip.com",
        full_name: "A zulip test bot",
        is_admin: false,
        is_bot: true,
        user_id: 1,
    }, {
        email: "a_user@zulip.org",
        full_name: "A zulip user",
        is_admin: false,
        is_bot: false,
        user_id: 2,
    }, {
        email: "b_user_1@zulip.net",
        full_name: "Bob 1",
        is_admin: false,
        is_bot: false,
        user_id: 3,
    }, {
        email: "b_user_2@zulip.net",
        full_name: "Bob 2",
        is_admin: true,
        is_bot: false,
        user_id: 4,
    }, {
        email: "b_bot@example.com",
        full_name: "B bot",
        is_admin: false,
        is_bot: true,
        user_id: 5,
    }, {
        email: "zman@test.net",
        full_name: "Zman",
        is_admin: false,
        is_bot: false,
        user_id: 6,
    },
];

_.each(matches, function (person) {
    global.people.add_in_realm(person);
});

(function test_sort_recipients() {
    function get_typeahead_result(query, current_stream) {
        var result = th.sort_recipients(
            global.people.get_realm_persons(),
            query,
            current_stream
        );
        return _.map(result, function (person) {
            return person.email;
        });
    }

    assert.deepEqual(get_typeahead_result("b", ""), [
        'b_user_1@zulip.net',
        'b_user_2@zulip.net',
        'b_bot@example.com',
        'a_user@zulip.org',
        'zman@test.net',
        'a_bot@zulip.com',
     ]);

    var subscriber_email = "b_user_2@zulip.net";
    stream_data.add_subscriber("Dev", people.get_user_id(subscriber_email));
    assert.deepEqual(get_typeahead_result("b", "Dev"), [
        subscriber_email,
        'b_user_1@zulip.net',
        'b_bot@example.com',
        'a_user@zulip.org',
        'zman@test.net',
        'a_bot@zulip.com',
    ]);

    // No match
    assert.deepEqual(get_typeahead_result("h", "Linux"), [
        'a_user@zulip.org',
        'b_user_1@zulip.net',
        'b_user_2@zulip.net',
        'zman@test.net',
        'a_bot@zulip.com',
        'b_bot@example.com',
    ]);

    // Test person email is "all" or "everyone"
    var person = {
        email: "all",
        full_name: "All",
        is_admin: false,
        is_bot: false,
        user_id: 42,
    };
    people.add_in_realm(person);

    assert.deepEqual(get_typeahead_result("a", "Linux"), [
        'all',
        'a_user@zulip.org',
        'a_bot@zulip.com',
        'b_user_1@zulip.net',
        'b_user_2@zulip.net',
        'zman@test.net',
        'b_bot@example.com',
    ]);

    people.deactivate(person);

    // Test sort_recipients with pm counts
    matches[0].pm_recipient_count = 50;
    matches[1].pm_recipient_count = 2;
    matches[2].pm_recipient_count = 32;
    matches[3].pm_recipient_count = 42;
    matches[4].pm_recipient_count = 0;
    matches[5].pm_recipient_count = 1;

    assert.deepEqual(get_typeahead_result("b", "Linux"), [
        'b_user_2@zulip.net',
        'b_user_1@zulip.net',
        'b_bot@example.com',
        'a_bot@zulip.com',
        'a_user@zulip.org',
        'zman@test.net',
    ]);

    // Test sort_recipients with duplicate people
    matches.push(matches[0]);

    var recipients = th.sort_recipients(matches, "b", "Linux");
    var recipients_email = _.map(recipients, function (person) {
        return person.email;
    });
    var expected = [
        'b_user_2@zulip.net',
        'b_user_1@zulip.net',
        'b_bot@example.com',
        'a_bot@zulip.com',
        'a_bot@zulip.com',
        'a_user@zulip.org',
        'zman@test.net',
    ];
    assert.deepEqual(recipients_email, expected);

    // Reset matches
    matches.splice(matches.length-1, 1);
}());

(function test_highlight_with_escaping() {
    var item = "Denmark";
    var query = "Den";
    var expected = "Denmark";
    var result = th.highlight_with_escaping(query, item);
    assert.equal(result, expected);

    item = "w3IrD_naMe";
    query = "w3IrD_naMe";
    expected = "w3IrD_naMe";
    result = th.highlight_with_escaping(query, item);
    assert.equal(result, expected);

    item = "development help";
    query = "development h";
    expected = "development help";
    result = th.highlight_with_escaping(query, item);
    assert.equal(result, expected);
}());

(function test_render_person() {
    // Test render_person with regular person
    var rendered = false;
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, matches[1].full_name);
        assert.equal(args.secondary, matches[1].email);
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_person(matches[1]), 'typeahead-item-stub');
    assert(rendered);

    // Test render_person with special_item_text person
    var special_person = {
        email: "special@example.com",
        full_name: "Special person",
        is_admin: false,
        is_bot: false,
        user_id: 7,
        special_item_text: "special_text",
    };
    rendered = false;
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, special_person.special_item_text);
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_person(special_person), 'typeahead-item-stub');
    assert(rendered);
}());

(function test_render_stream() {
    // Test render_stream with short description
    var rendered = false;
    var stream = {
        description: 'This is a short description.',
        stream_id: 42,
        name: 'Short Description',
    };
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, stream.name);
        assert.equal(args.secondary, stream.description);
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_stream(stream), 'typeahead-item-stub');
    assert(rendered);

    // Test render_stream with long description
    rendered = false;
    stream = {
        description: 'This is a very very very very very long description.',
        stream_id: 43,
        name: 'Long Description',
    };
    global.templates.render = function (template_name, args) {
        assert.equal(template_name, 'typeahead_list_item');
        assert.equal(args.primary, stream.name);
        var short_desc = stream.description.substring(0, 35);
        assert.equal(args.secondary, short_desc + "...");
        rendered = true;
        return 'typeahead-item-stub';
    };
    assert.equal(th.render_stream(stream), 'typeahead-item-stub');
    assert(rendered);
}());

(function test_sort_emojis() {
    var emoji_list = [
        { emoji_name: '+1' },
        { emoji_name: 'thumbs_up' },
        { emoji_name: 'pig' },
        { emoji_name: 'thumbs_down' },
    ];
    assert.deepEqual(th.sort_emojis(emoji_list, 'thumbs'), [
        { emoji_name: 'thumbs_up' },
        { emoji_name: 'thumbs_down' },
        { emoji_name: '+1' },
        { emoji_name: 'pig' },
    ]);
}());
<|endoftext|>