Source: parts/saver/saver-postgres.js

"use strict";

var fs = require('fs');
var util = require('util');
var pg = require('pg');
var _ = require('lodash');

var debug = require("debug");
var logDev = debug('dbupdater:saver:postgres:dev');
var log = debug('dbupdater:saver:postgres:log');
var logWarn = debug('dbupdater:saver:postgres:warn');
var logErr = debug('dbupdater:saver:postgres:err');

var TaskSaverAbstract = require('./saver-abstract');

var DEF_CONFIG = {
    dbTable: 'tasks'
};

/**
 * Транспорт фиксации задач для PostgreSQL. Создается таблица config.dbTable и в ней фиксируются выполненные задачи
 * @param {object} config - параметры инициализации транспорта
 * @param {string} [config.dbTable=tasks] - имя таблицы в БД, куда будут записываться все выполненные апдейты
 * @param {string} config.connString - строка подключения к БД
 * @returns {Saver.TaskSaverPostgreSQL}
 * @constructor
 * @augments Saver.TaskSaverAbstract
 * @memberof Saver
 */
function TaskSaverPostgreSQL (config) {
    var self = this;

    if (self instanceof TaskSaverPostgreSQL === false) {
        return new TaskSaverPostgreSQL(config);
    }

    self.config = _.defaults(config || {}, DEF_CONFIG);
    logDev('Config %j', self.config);
}

TaskSaverPostgreSQL.prototype = new TaskSaverAbstract();

/**
 * Метод инициализирующий транспорт. Проверяет коннект, создает таблицу в БД, если её нет.
 * @param {function} cb - колбэк инициализации
 * @param {?Error} cb.err - Ошибка инициализации
 */
TaskSaverPostgreSQL.prototype.init = function init (cb) {
    var self = this,
        client,
        err;

    if (!self.config.connString) {
        err = new Error('parametr "connString" is not set at taskSaver_postgresql config');
        logErr(err);
        cb(err);
        return;
    }

    client = new pg.Client(self.config.connString);
    client.connect(function(err) {
        if(err) {
            client.end();
            logErr(err);
            cb(err);
            return;
        }
        logDev('DB connected');

        client.query("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';", function(err, result) {
            var sql;

            if(err) {
                client.end();
                logErr(err);
                cb(err);
                return;
            }
            if (_.findIndex(result.rows, 'table_name', self.config.dbTable) === -1) {
                logDev('Task table "%s" not found, need to create it', self.config.dbTable);
                // Нужно добавить табличку

                sql = util.format('CREATE TABLE "%s" (' +
                    '   name character varying(256) NOT NULL, ' +
                    '   md5 character varying(128) NOT NULL, ' +
                    '   executed timestamp without time zone NOT NULL DEFAULT now(),' +
                    '   PRIMARY KEY (name)' +
                    ');',
                    self.config.dbTable
                );

                client.query(sql, function(err, result) {
                    if(err) {
                        client.end();
                        logErr(err);
                        cb(err);
                        return;
                    }

                    log('Task table "%s" is created', self.config.dbTable);
                    client.end();
                    cb();
                });

            } else {
                // Табличка есть, все ок
                client.end();
                logDev('Task table "%s" founded', self.config.dbTable);
                cb();
            }
        });
    });
};

/**
 * Получает список выполненных задач из БД
 * @param {function} cb - колбэк получения списка ваполненныз задач
 * @param {?Error} cb.err - ошибка выполнения
 * @param {?object[]} cb.tasks - Массив выполненных задач
 * @param {string} cb.tasks.name - имя выполненной задачи
 * @param {string} cb.tasks.md5 - md5 сумма выполненной задачи (защита от подмены/изменения)
 * @param {string} cb.tasks.executed - когда была выполнен задача
 */
TaskSaverPostgreSQL.prototype.getTasks = function taskSaverInit (cb) {
    var self = this,
        client;

    client = new pg.Client(self.config.connString);
    client.connect(function(err) {
        var sql;

        if (err) {
            client.end();
            logErr(err);
            cb(err);
            return;
        }
        logDev('DB connected');

        sql = util.format('SELECT "name", "md5", "executed" FROM "%s"', self.config.dbTable);
        client.query(sql, function (err, result) {
            if (err) {
                client.end();
                logErr(err);
                cb(err);
                return;
            }

            // Убираем промежуточные данные и формируем ISO строку
            result = _.map(result.rows, function (el) {
                el.executed = el.executed.toISOString();
                return el;
            });

            client.end();
            logDev('Executed tasks %j', result);
            cb(null, result);
        });
    });
};

/**
 * Записать в БД, что задача была выполнена
 * @param {object} task - выполненная задача
 * @param {string} task.name - имя выполенной задачи
 * @param {string} task.md5 - контрольная сумма текста задачи
 * @param {function} cb - колбэк записи задачи в БД
 * @param {?Error} cb.err - ошибка записи
 */
TaskSaverPostgreSQL.prototype.logExecutedTask = function (task, cb) {
    var self = this,
        client;

    client = new pg.Client(self.config.connString);
    client.connect(function(err) {
        var sql;

        if (err) {
            client.end();
            logErr(err);
            cb(err);
            return;
        }
        logDev('DB connected');

        sql = util.format('INSERT INTO "%s" (name, md5) VALUES ($1, $2);', self.config.dbTable);
        client.query(sql, [task.name, task.md5], function (err, result) {
            if (err) {
                client.end();
                logErr(err);
                cb(err);
                return;
            }

            client.end();
            cb(null, result);
        });
    });
};

module.exports = TaskSaverPostgreSQL;