anti-same-day-dupe-oo.js 7.58 KB
var redis = require('redis');
var LRU = require('lru-cache');
var moment = require('moment');

var maxCacheCount = 20;

module.exports = AntiSameDayDupe;

function AntiSameDayDupe(options) {
    var config;
    var logger;
    var redisClient;
    var keyPrefix = '';

    var taskCache = LRU({max: maxCacheCount, maxAge: 1000 * 3600 * 2});
    var reverseCache = LRU({max: maxCacheCount, maxAge: 1000 * 3600 * 2});

    if (!options) {
        console.log('Undefined options, terminating....');
        process.exit(1);
    }

    if (options.config) {
        config = options.config;
    } else {
        console.log('Undefined options.config, terminating....')
        process.exit(1);
    }

    if (options && options.logger) {
        logger = options.logger;
    } else {
        console.log('Undefined options.logger, terminating....')
        process.exit(1);
    }

    if (options && options.redisClient) {
        redisClient = options.redisClient;
    } else {
        console.log('Undefined options.redisClient, terminating....')
        process.exit(1);
    }

    if (options.keyPrefix) {
        keyPrefix = options.keyPrefix;
    }

    logger.info('AntiSameDayDupe (OO) initialized');

    function getKey(task, chipInfo) {
        var today = moment(task.timestamp, 'YYYYMMDDHHmmss').format('YYYYMMDD');
        return keyPrefix + chipInfo + '.antiSameDayDupe2.trx.date:' + today + '.rProd:' + task.remoteProduct.toUpperCase() + '.dest:' + task.destination ;
    }

    function getReverseKey(task, chipInfo) {
        var requestId;

        if (typeof task == 'string') {
            requestId = task;
        } else {
            requestId = task.requestId;
        }

        return keyPrefix + chipInfo + '.antiSameDayDupe2.rid:' + requestId;
    }

    function register(_task, cb) {

        var task = JSON.parse(JSON.stringify(_task));
        var key = getKey(task, config.globals.gateway_name);
        var rKey = getReverseKey(task, config.globals.gateway_name);

        logger.verbose('Registering antiSameDayDupe2 entry', {key: key, rkey: rKey, task: task});

        taskCache.set(key, task);
        reverseCache.set(rKey, key);
        saveToRedis(task, cb);

    }

    function unregister(requestId) {

        var rKey = getReverseKey(requestId, config.globals.gateway_name);
        getKeyByRequestId(requestId, function(key) {
            if (!key) {
                logger.warn('antiSameDayDupe2: Undefined related key, unregister aborted', {requestId: requestId, rKey: rKey});
                return;
            }

            try {
                taskCache.del(key);
                reverseCache.del(rKey);
                logger.verbose('Task has been deleted from taskCache', {requestId: requestId, rKey: rKey, key: key});
            }
            catch(e) {
                logger.warn('antiSameDayDupe2: exception when delete task from taskCache', {requestId: requestId, key: key, error: e});
            }

            try {
                redisClient.del(key, rKey);
                logger.verbose('Task has been deleted from redis', {requestId: requestId, rKey: rKey, key: key});
            }
            catch(e) {
                logger.warn('antiSameDayDupe2: exception when delete task from redis', {requestId: requestId, key: key, error: e});

            }
        });

    }

    function saveToRedis(task, cb) {
        var key = getKey(task, config.globals.gateway_name);
        var rKey = getReverseKey(task, config.globals.gateway_name);

        logger.verbose('antiSameDayDupe2: Saving task', {key: key, rkey: rKey, task: task});

        redisClient.mset(key, JSON.stringify(task), rKey, key, function() {
            redisClient.expire(key, 3600*24);
            redisClient.expire(rKey, 3600*24);

            if (cb) {
                cb();
            }
        });
    }

    function deleteFromRedis(task, cb) {
        var key = getKey(task, config.globals.gateway_name);
        var rKey = getReverseKey(task, config.globals.gateway_name);

        logger.verbose('antiSameDayDupe2: Delete task from redis', {key: key, task: task});

        redisClient.del(key, function() {
            if (cb) {
                cb();
            }
        });
    }

    function createDummyTask(remoteProduct, destination) {
        return {
            remoteProduct: remoteProduct,
            destination: destination,
            timestamp: moment().format('YYYYMMDD'),
        }
    }

    function get(remoteProduct, destination, cb) {
        var dummyTask = createDummyTask(remoteProduct, destination);

        var key = getKey(dummyTask, config.globals.gateway_name);
        var task = taskCache.get(key);

        if (task) {
            cb(null, task);
        }
        else {
            getFromRedis(remoteProduct, destination, cb);
        }
    }

    function getFromRedis(remoteProduct, destination, cb) {
        var dummyTask = createDummyTask(remoteProduct, destination);

        var key = getKey(dummyTask, config.globals.gateway_name, moment().format('YYYYMMDD'));
        redisClient.get(key, function(err, result) {
            if (err) {
                logger.warn('antiDupe.get: error getting task from redis', {key: key, params: dummyTask});

                cb(err, null);
                return;
            }

            var task = {};

            try {
                task = JSON.parse(result);
            }
            catch(e) {
                logger.warn('antiDupe.get: Can not parse result', {key: key, params: dummyTask, data: result});
                err = "Can not parse task";
                cb(err, null);
                return;
            }

            cb(err, task);
        });
    }

    function check(task, cbNoDupe, cbDupe, cbDupeWithSameReqId) {
        if (Number(config.globals.no_same_day_dupe_check)) {
            logger.verbose('Skipping same day dupe check because of config.globals.no_same_day_dupe_check');
            cbNoDupe(task);
            return;
        }

        get(task.remoteProduct, task.destination, function(err, archivedTask) {
            if (err) {
                logger.warn('Error on checking same day duplicate', {task: task});
                cbNoDupe(task);
                return;
            }

            if (archivedTask && archivedTask.requestId) {
                if (task.requestId == archivedTask.requestId) {
                    logger.verbose('Duplicate trx on same day with same requestId', {task: task});
                    cbDupeWithSameReqId(task);
                    return;
                }

                logger.verbose('Duplicate trx on same day', {task: task, archivedTask: archivedTask});
                cbDupe(task);
                return;
            }

            register(task, function() {
                cbNoDupe(task);
            });
        });
    }

    function getKeyByRequestId(requestId, cb) {
        var rKey = getReverseKey(requestId);

        var key = reverseCache.get(rKey);
        if (key) {

            logger.verbose('antiSameDayDupe2: Got key from cache', {requestId: requestId, rKey: rKey, key: key});
            if (cb) { cb(key); }

        } else {

            getKeyByRequestIdFromRedis(requestId, cb);

        }
    }

    function getKeyByRequestIdFromRedis(requestId, cb) {
        var rKey = getReverseKey(requestId, config.globals.gateway_name);
        redisClient.get(rKey, function(err, result) {
            if (err) {
                logger.warn('antiSameDayDupe2: error getting key from redis', {requestId: requestId, rKey: rKey});

                cb(null);
                return;
            }

            cb(result);
        });
    }

    return {
        check: check,
        unregister: unregister
    }
}