<commit_before>/**
 *
 * @licstart  The following is the entire license notice for the JavaScript code in this file.
 *
 * Melinda related modules for recordLoader
 *
 * Copyright (c) 2016 University Of Helsinki (The National Library Of Finland)
 *
 * This file is part of record-loader-melinda
 *
 * record-loader-melinda 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @licend  The above is the entire license notice
 * for the JavaScript code in this file.
 *
 **/

/* istanbul ignore next: umd wrapper */
(function (root, factory) {

  'use strict';

  if (typeof define === 'function' && define.amd) {
    define([
      'es6-polyfills/lib/polyfills/promise',
      'es6-polyfills/lib/polyfills/object',
      'es6-shims/lib/shims/array',
      'marc-record-js',
      'loglevel',
      'jjv',
      'jjve',
      'record-loader-prototypes/lib/hooks/prototype',
      '../../../../resources/hook-after-wait-merge-host-components-relations-schema.json'
    ], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory(
      require('es6-polyfills/lib/polyfills/promise'),
      require('es6-polyfills/lib/polyfills/object'),
      require('es6-shims/lib/shims/array'),
      require('marc-record-js'),
      require('loglevel'),
      require('jjv'),
      require('jjve'),
      require('record-loader-prototypes/lib/hooks/prototype'),
      require('../../../../resources/hook-after-wait-merge-host-components-relations-schema.json')
    );
  }

}(this, factory));

function factory(Promise, Object, shim_array, MarcRecord, log, jjv, jjve, protoFactory, schema)
{

  'use strict';

  return function(parameters)
  {

    function validateParameters()
    {

      var env = jjv(),
      je = jjve(env),
      errors = env.validate(schema, parameters, {
        useDefault: true
      });

      if (errors) {
        throw new Error('Invalid parameters: ' + JSON.stringify(je(schema, parameters, errors), undefined, 4));
      }

    }

    var record_store, record_set, converter, logger, record_loader_results,
    proto = protoFactory();

    parameters = typeof parameters === 'object' ? parameters : {};

    validateParameters();

    return Object.assign(proto, {
      setLogger: function(logger_arg)
      {
        logger = logger_arg;
      },
      setRecordStore: function(record_store_arg)
      {
        record_store = record_store_arg;
      },
      setRecordSet: function(record_set_arg)
      {
        record_set = record_set_arg;
      },
      setConverter: function(converter_arg)
      {
        converter = converter_arg;
      },
      setResults: function(results_arg)
      {
        record_loader_results = results_arg;
      },
      run: function(results) {

        function applyDetails(key, index, data)
        {

          if (!details.records.hasOwnProperty(index)) {
            details.records[index] = {};
          }

          if (!details.records[index].hasOwnProperty(key)) {
            details.records[index][key] = data;
          } else {
            Object.assign(details.records[index][key], data);
          }

        }

        /**
         * @internal Attempts to remove extraneous, obsolete matches and find missing ones
         **/
        function normalizeMatches(records_store)
        {

          function removeMultimatches()
          {

            /**
             * @internal Remove matches that already are a singular match for some other record
             **/
            results_obj.results.forEach(function(result1) {

              var records_removed = [],
              found_indexes = [];

              if (result1.matches.length > 1) {

                result1.matches.forEach(function(record_matched, index) {

                  var record_matched_string = JSON.stringify(record_matched.toJsonObject());

                  results_obj.results.some(function(result2) {

                    if (result2 !== result1 && result2.matches.length === 1 && record_matched_string === JSON.stringify(result2.matches[0].toJsonObject()))

                      if (found_indexes.indexOf(index) >= 0) {
                        found_indexes.splice(found_indexes.indexOf(index), 1);
                        return true;
                      } else {
                        found_indexes.push(index);
                      }

                  });
                });

                if (found_indexes.length > 0) {

                  result1.matches = result1.matches.filter(function(element, index) {
                    if (found_indexes.indexOf(index) < 0) {
                      records_removed.push(element);
                    } else {
                      return 1;
                    }
                  });

                  applyDetails(result1.index, 'normalization', {
                    multimatchesRemoved: records_removed
                  });

                  logger.debug('Removed multimatches from record ' + result1.index);

                }

              }

            });

          }

          function findMissing()
          {

            var found_index_results, found_index_store;

            /**
             * @internal Only continue if there is a single record without any matches
             **/
            if (!results_obj.results.some(function(result, index) {
              if (result.matches.length === 0) {

                if (found_index_results === undefined) {
                  found_index_results = index;
                } else {
                  return true;
                }

              }
            }) && found_index_results !== undefined) {

              /**
               * @internal If the single unmatching record is a host match it to the record store host
               **/
              if (results_obj.results[found_index_results].record.get(/^773$/).length === 0) {

                results_obj.results[found_index_results].matches.push(shim_array.find(records_store, function(record) {

                  return record.get(/^001$/)[0].value === results_obj.recordStoreHost;

                }));

                logger.debug('Found the missing match (Host) for record ' + results_obj.results[found_index_results].index);

                applyDetails(results_obj.results[found_index_results].index, 'normalization', {
                  singleMissingMatchFound: true
                });

                /**
                 * @internal Check that there is only one missing record in the store
                 **/
              } else if (!records_store.some(function(record, index) {

                var record_matchless_string = JSON.stringify(record.toJsonObject());

                if (!results_obj.results.some(function(result) {

                  return result.matches.map(function(record) {
                    return JSON.stringify(record.toJsonObject());
                  }).some(function(record_string) {
                    return record_string === record_matchless_string;
                  });

                })) {

                  if (found_index_store === undefined) {
                    found_index_store = index;
                  } else {
                    return true;
                  }

                }

              }) && found_index_store !== undefined) {

                results_obj.results[found_index_results].matches.push(records_store[found_index_store]);

                logger.debug('Found the missing match for record ' + results_obj.results[found_index_results].index);

                applyDetails(results_obj.results[found_index_results].index, 'normalization', {
                  singleMissingMatchFound: true
                });

              }

            }

          }

          /**
           * @internal Attempts to find matches based on record id index
           */
          function findMissingByIndex()
          {
            results_obj.results.forEach(function(result, index) {

              var id, id_prev_store, id_next_store, record_store_match;

              if (result.matches.length === 0) {

                id = Number(result.record.get(/^001$/).shift().value);

                if (results_obj.results.some(function(result_some) {

                  var id_other;

                  if (result_some !== result && result_some.matches.length === 1) {

                    id_other = Number(result_some.record.get(/^001$/).shift().value);

                    if (id_other === id - 1) {
                      id_prev_store = Number(result_some.matches[0].get(/^001$/).shift().value);
                    } else if (id_other === id + 1) {
                      id_next_store = Number(result_some.matches[0].get(/^001$/).shift().value);
                    }

                    if (id_prev_store !== undefined && id_next_store !== undefined) {
                      return 1;
                    }

                  }

                })) {

                  record_store_match = shim_array.find(records_store, function(record) {

                    var id_store = Number(record.get(/^001$/).shift().value);

                    return id_prev_store === id_store - 1 && id_next_store === id_store + 1;

                  });

                  if (record_store_match) {

                    result.matches.push(record_store_match);

                    logger.debug('Found the missing match for record ' + results_obj.results[index].index + ' by record id index');

                    applyDetails(results_obj.results[index].index, 'normalization', {
                      foundMissingByIndex: record_store_match.toJsonObject()
                    });

                  }

                }

              }

            });
          }

          removeMultimatches();

          if (results_obj.results.some(function(result) {
            return result.matches.length > 1;
          })) {
            throw new Error('Multimatches not allowed');
          } else {

            if (parameters.findMissingByIndex === true && results_obj.results.some(function(result) {
              return result.matches.length > 0;
            })) {
              findMissingByIndex();
            }

            findMissing();

            if (results_obj.results.some(function(result) {
              return result.matches.length === 0;
            })) {
              throw new Error('Records without matches not allowed');
            }

          }

        }

        /**
         * @internal Check is there are too many extraneous records in either side (record set / store)
         **/
        function checkDifference(records_store)
        {

          function testTreshold(key)
          {
            if (typeof parameters.difference[key] !== 'number' || Math.abs(details.difference) > parameters.difference[key]) {
              throw new Error('Too many extraneous records between corresponding host-component sets');
            }
          }

          details.difference = results_obj.results.length - records_store.length;

          logger.debug('Records in set: ' + results_obj.results.length + ', records in store: ' + records_store.length);

          if (details.difference < 0) {
            testTreshold('recordStore');
          } else if (details.difference > 0) {
            testTreshold('recordSet');
          }

        }

        function checkUniqueMatches()
        {
          if (results_obj.results.some(function(result) {

            return results_obj.results.some(function(result2) {
              return result !== result2 && JSON.stringify(result.matches[0]) === JSON.stringify(result2.matches[0]);
            });

          })) {
            throw new Error('Non-unique matches not allowed');
          }
        }

        var details, results_obj;

        if (!record_loader_results.hasOwnProperty('hooks')) {
          record_loader_results.hooks = {};
        }

        if (!record_loader_results.hooks.hasOwnProperty('afterWait')) {
          record_loader_results.hooks.afterWait = {};
        }

        record_loader_results.hooks.afterWait.merge = {
          records: {}
        };

        details = record_loader_results.hooks.afterWait.merge;
        results_obj = {
          recordStoreHost: results[0].results.data.input.recordStoreHost,
          results: results.map(function(result) {

            return {
              record: result.stepResults[0].record,
              index: result.results.index,
              matches: result.stepResults[1]
            };

          })
        };

        /**
         * @internal Get the corresponding records from the store
         **/
        return record_store.read(results_obj.recordStoreHost, 1).then(function(records_store) {

          normalizeMatches(records_store);
          checkUniqueMatches();
          checkDifference(records_store);

          /**
           * @internal Update 'matched' property
           **/
          results.forEach(function(result) {
            result.results.matched = result.stepResults[1].map(function(record) {
              return new MarcRecord(record);
            });
          });

        });

      }
    });

  };

}
<commit_msg>Create MARC records from plain record data<commit_after>/**
 *
 * @licstart  The following is the entire license notice for the JavaScript code in this file.
 *
 * Melinda related modules for recordLoader
 *
 * Copyright (c) 2016 University Of Helsinki (The National Library Of Finland)
 *
 * This file is part of record-loader-melinda
 *
 * record-loader-melinda 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.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @licend  The above is the entire license notice
 * for the JavaScript code in this file.
 *
 **/

/* istanbul ignore next: umd wrapper */
(function (root, factory) {

  'use strict';

  if (typeof define === 'function' && define.amd) {
    define([
      'es6-polyfills/lib/polyfills/promise',
      'es6-polyfills/lib/polyfills/object',
      'es6-shims/lib/shims/array',
      'marc-record-js',
      'loglevel',
      'jjv',
      'jjve',
      'record-loader-prototypes/lib/hooks/prototype',
      '../../../../resources/hook-after-wait-merge-host-components-relations-schema.json'
    ], factory);
  } else if (typeof module === 'object' && module.exports) {
    module.exports = factory(
      require('es6-polyfills/lib/polyfills/promise'),
      require('es6-polyfills/lib/polyfills/object'),
      require('es6-shims/lib/shims/array'),
      require('marc-record-js'),
      require('loglevel'),
      require('jjv'),
      require('jjve'),
      require('record-loader-prototypes/lib/hooks/prototype'),
      require('../../../../resources/hook-after-wait-merge-host-components-relations-schema.json')
    );
  }

}(this, factory));

function factory(Promise, Object, shim_array, MarcRecord, log, jjv, jjve, protoFactory, schema)
{

  'use strict';

  return function(parameters)
  {

    function validateParameters()
    {

      var env = jjv(),
      je = jjve(env),
      errors = env.validate(schema, parameters, {
        useDefault: true
      });

      if (errors) {
        throw new Error('Invalid parameters: ' + JSON.stringify(je(schema, parameters, errors), undefined, 4));
      }

    }

    var record_store, record_set, converter, logger, record_loader_results,
    proto = protoFactory();

    parameters = typeof parameters === 'object' ? parameters : {};

    validateParameters();

    return Object.assign(proto, {
      setLogger: function(logger_arg)
      {
        logger = logger_arg;
      },
      setRecordStore: function(record_store_arg)
      {
        record_store = record_store_arg;
      },
      setRecordSet: function(record_set_arg)
      {
        record_set = record_set_arg;
      },
      setConverter: function(converter_arg)
      {
        converter = converter_arg;
      },
      setResults: function(results_arg)
      {
        record_loader_results = results_arg;
      },
      run: function(results) {

        function applyDetails(key, index, data)
        {

          if (!details.records.hasOwnProperty(index)) {
            details.records[index] = {};
          }

          if (!details.records[index].hasOwnProperty(key)) {
            details.records[index][key] = data;
          } else {
            Object.assign(details.records[index][key], data);
          }

        }

        /**
         * @internal Attempts to remove extraneous, obsolete matches and find missing ones
         **/
        function normalizeMatches(records_store)
        {

          function removeMultimatches()
          {

            /**
             * @internal Remove matches that already are a singular match for some other record
             **/
            results_obj.results.forEach(function(result1) {

              var records_removed = [],
              found_indexes = [];

              if (result1.matches.length > 1) {

                result1.matches.forEach(function(record_matched, index) {

                  var record_matched_string = JSON.stringify(record_matched.toJsonObject());

                  results_obj.results.some(function(result2) {

                    if (result2 !== result1 && result2.matches.length === 1 && record_matched_string === JSON.stringify(result2.matches[0].toJsonObject()))

                      if (found_indexes.indexOf(index) >= 0) {
                        found_indexes.splice(found_indexes.indexOf(index), 1);
                        return true;
                      } else {
                        found_indexes.push(index);
                      }

                  });
                });

                if (found_indexes.length > 0) {

                  result1.matches = result1.matches.filter(function(element, index) {
                    if (found_indexes.indexOf(index) < 0) {
                      records_removed.push(element);
                    } else {
                      return 1;
                    }
                  });

                  applyDetails(result1.index, 'normalization', {
                    multimatchesRemoved: records_removed
                  });

                  logger.debug('Removed multimatches from record ' + result1.index);

                }

              }

            });

          }

          function findMissing()
          {

            var found_index_results, found_index_store;

            /**
             * @internal Only continue if there is a single record without any matches
             **/
            if (!results_obj.results.some(function(result, index) {
              if (result.matches.length === 0) {

                if (found_index_results === undefined) {
                  found_index_results = index;
                } else {
                  return true;
                }

              }
            }) && found_index_results !== undefined) {

              /**
               * @internal If the single unmatching record is a host match it to the record store host
               **/
              if (results_obj.results[found_index_results].record.get(/^773$/).length === 0) {

                results_obj.results[found_index_results].matches.push(shim_array.find(records_store, function(record) {

                  return record.get(/^001$/)[0].value === results_obj.recordStoreHost;

                }));

                logger.debug('Found the missing match (Host) for record ' + results_obj.results[found_index_results].index);

                applyDetails(results_obj.results[found_index_results].index, 'normalization', {
                  singleMissingMatchFound: true
                });

                /**
                 * @internal Check that there is only one missing record in the store
                 **/
              } else if (!records_store.some(function(record, index) {

                var record_matchless_string = JSON.stringify(record.toJsonObject());

                if (!results_obj.results.some(function(result) {

                  return result.matches.map(function(record) {
                    return JSON.stringify(record.toJsonObject());
                  }).some(function(record_string) {
                    return record_string === record_matchless_string;
                  });

                })) {

                  if (found_index_store === undefined) {
                    found_index_store = index;
                  } else {
                    return true;
                  }

                }

              }) && found_index_store !== undefined) {

                results_obj.results[found_index_results].matches.push(records_store[found_index_store]);

                logger.debug('Found the missing match for record ' + results_obj.results[found_index_results].index);

                applyDetails(results_obj.results[found_index_results].index, 'normalization', {
                  singleMissingMatchFound: true
                });

              }

            }

          }

          /**
           * @internal Attempts to find matches based on record id index
           */
          function findMissingByIndex()
          {
            results_obj.results.forEach(function(result, index) {

              var id, id_prev_store, id_next_store, record_store_match;

              if (result.matches.length === 0) {

                id = Number(result.record.get(/^001$/).shift().value);

                if (results_obj.results.some(function(result_some) {

                  var id_other;

                  if (result_some !== result && result_some.matches.length === 1) {

                    id_other = Number(result_some.record.get(/^001$/).shift().value);

                    if (id_other === id - 1) {
                      id_prev_store = Number(result_some.matches[0].get(/^001$/).shift().value);
                    } else if (id_other === id + 1) {
                      id_next_store = Number(result_some.matches[0].get(/^001$/).shift().value);
                    }

                    if (id_prev_store !== undefined && id_next_store !== undefined) {
                      return 1;
                    }

                  }

                })) {

                  record_store_match = shim_array.find(records_store, function(record) {

                    var id_store = Number(record.get(/^001$/).shift().value);

                    return id_prev_store === id_store - 1 && id_next_store === id_store + 1;

                  });

                  if (record_store_match) {

                    result.matches.push(record_store_match);

                    logger.debug('Found the missing match for record ' + results_obj.results[index].index + ' by record id index');

                    applyDetails(results_obj.results[index].index, 'normalization', {
                      foundMissingByIndex: record_store_match.toJsonObject()
                    });

                  }

                }

              }

            });
          }

          removeMultimatches();

          if (results_obj.results.some(function(result) {
            return result.matches.length > 1;
          })) {
            throw new Error('Multimatches not allowed');
          } else {

            if (parameters.findMissingByIndex === true && results_obj.results.some(function(result) {
              return result.matches.length > 0;
            })) {
              findMissingByIndex();
            }

            findMissing();

            if (results_obj.results.some(function(result) {
              return result.matches.length === 0;
            })) {
              throw new Error('Records without matches not allowed');
            }

          }

        }

        /**
         * @internal Check is there are too many extraneous records in either side (record set / store)
         **/
        function checkDifference(records_store)
        {

          function testTreshold(key)
          {
            if (typeof parameters.difference[key] !== 'number' || Math.abs(details.difference) > parameters.difference[key]) {
              throw new Error('Too many extraneous records between corresponding host-component sets');
            }
          }

          details.difference = results_obj.results.length - records_store.length;

          logger.debug('Records in set: ' + results_obj.results.length + ', records in store: ' + records_store.length);

          if (details.difference < 0) {
            testTreshold('recordStore');
          } else if (details.difference > 0) {
            testTreshold('recordSet');
          }

        }

        function checkUniqueMatches()
        {
          if (results_obj.results.some(function(result) {

            return results_obj.results.some(function(result2) {
              return result !== result2 && JSON.stringify(result.matches[0]) === JSON.stringify(result2.matches[0]);
            });

          })) {
            throw new Error('Non-unique matches not allowed');
          }
        }

        var details, results_obj;

        if (!record_loader_results.hasOwnProperty('hooks')) {
          record_loader_results.hooks = {};
        }

        if (!record_loader_results.hooks.hasOwnProperty('afterWait')) {
          record_loader_results.hooks.afterWait = {};
        }

        record_loader_results.hooks.afterWait.merge = {
          records: {}
        };

        details = record_loader_results.hooks.afterWait.merge;
        results_obj = {
          recordStoreHost: results[0].results.data.input.recordStoreHost,
          results: results.map(function(result) {

            return {
              record: new MarcRecord(result.stepResults[0].record),
              index: result.results.index,
              matches: result.stepResults[1].map(function(record) {
                return new MarcRecord(record);
              })
            };

          })
        };

        /**
         * @internal Get the corresponding records from the store
         **/
        return record_store.read(results_obj.recordStoreHost, 1).then(function(records_store) {

          normalizeMatches(records_store);
          checkUniqueMatches();
          checkDifference(records_store);

          /**
           * @internal Update 'matched' property
           **/
          results.forEach(function(result) {
            result.results.matched = result.stepResults[1].map(function(record) {
              return new MarcRecord(record);
            });
          });

        });

      }
    });

  };

}
<|endoftext|>


<commit_before>/*global describe, it, beforeEach */
'use strict';

var expect = require('chai').expect;
var exec = require('child_process').exec;

var KyotoTocoon = require('../index');

describe('kt-client', function() {
  describe('get test', function() {
    beforeEach(function(done) {
      exec('ktremotemgr clear', function () {
        done();
      });
    });

    it('data', function(done) {
      exec('ktremotemgr set test_key test_value', function () {
        var client = new KyotoTocoon();
        client.get('test_key', function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('utf-8 data', function(done) {
      exec('ktremotemgr set test_key test_value', function () {
        var client = new KyotoTocoon();
        var options = {
          encoding: 'utf8'
        };
        client.get('test_key', options, function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('binary data', function(done) {
      exec('ktremotemgr set test_key binary', function () {
        var client = new KyotoTocoon();
        var options = {
          encoding: 'binary'
        };
        client.get('test_key', options, function(error, value, expire) {
          expect(Buffer.isBuffer(value)).to.be.true;
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('data and expiration time', function(done) {
      exec('ktremotemgr set -xt 300 test_key test_value', function () {
        var client = new KyotoTocoon();
        client.get('test_key', function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.an.instanceof(Date);
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('no data', function(done) {
      var client = new KyotoTocoon();
      client.get('test_key', function(error, value, expire) {
        expect(value).to.be.undefined;
        expect(expire).to.be.undefined;
        expect(error).to.equal('No record was found');
        done();
      });
    });

    it('connection error', function(done) {
      var client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });
      client.get('test_key', function(error, value, expire) {
        expect(value).to.be.undefined;
        expect(expire).to.be.undefined;
        expect(error).to.equal('Connection error');
        done();
      });
    });
  });
});
<commit_msg>Added test<commit_after>/*global describe, it, beforeEach */
'use strict';

var expect = require('chai').expect;
var exec = require('child_process').exec;

var KyotoTocoon = require('../index');

describe('kt-client', function() {
  describe('get test', function() {
    beforeEach(function(done) {
      exec('ktremotemgr clear', function () {
        done();
      });
    });

    it('data', function(done) {
      exec('ktremotemgr set test_key test_value', function () {
        var client = new KyotoTocoon();
        client.get('test_key', function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('utf-8 data', function(done) {
      exec('ktremotemgr set test_key test_value', function () {
        var client = new KyotoTocoon();
        var options = {
          encoding: 'utf8'
        };
        client.get('test_key', options, function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('binary data', function(done) {
      exec('ktremotemgr set test_key binary', function () {
        var client = new KyotoTocoon();
        var options = {
          encoding: 'binary'
        };
        client.get('test_key', options, function(error, value, expire) {
          expect(Buffer.isBuffer(value)).to.be.true;
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('data and expiration time', function(done) {
      exec('ktremotemgr set -xt 300 test_key test_value', function () {
        var client = new KyotoTocoon();
        client.get('test_key', function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.an.instanceof(Date);
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('no data', function(done) {
      var client = new KyotoTocoon();
      client.get('test_key', function(error, value, expire) {
        expect(value).to.be.undefined;
        expect(expire).to.be.undefined;
        expect(error).to.equal('No record was found');
        done();
      });
    });

    it('connection error', function(done) {
      var client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });
      client.get('test_key', function(error, value, expire) {
        expect(value).to.be.undefined;
        expect(expire).to.be.undefined;
        expect(error).to.equal('Connection error');
        done();
      });
    });
  });

  describe('set test', function() {
    beforeEach(function(done) {
      exec('ktremotemgr clear', function () {
        done();
      });
    });

    it('data', function(done) {
      var client = new KyotoTocoon();
      client.set('test_key', 'test_value', function(error) {
        expect(error).to.be.undefined;
        client.get('test_key', function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('utf-8 data', function(done) {
      var client = new KyotoTocoon();
      var options = {
        encoding: 'utf8'
      };
      client.set('test_key', 'test_value', options, function(error) {
        expect(error).to.be.undefined;
        client.get('test_key', options, function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('binary data', function(done) {
      var client = new KyotoTocoon();
      var options = {
        encoding: 'binary'
      };
      client.set('test_key', 'test_value', options, function(error) {
        expect(error).to.be.undefined;
        client.get('test_key', options, function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.null;
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('data and expiration time', function(done) {
      var client = new KyotoTocoon();
      var options = {
        expire: 300
      };
      client.set('test_key', 'test_value', options, function(error) {
        expect(error).to.be.undefined;
        client.get('test_key', function(error, value, expire) {
          expect(value).to.equal('test_value');
          expect(expire).to.be.an.instanceof(Date);
          expect(error).to.be.undefined;
          done();
        });
      });
    });

    it('connection error', function(done) {
      var client = new KyotoTocoon({
        host: 'localhost',
        port: 9999
      });
      client.set('test_key', 'test_value', function(error) {
        expect(error).to.equal('Connection error');
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

async function setEndpoint(projectId) {
  // [START automl_set_endpoint]
  const automl = require('@google-cloud/automl').v1beta1;

  // You must first create a dataset, using the `eu` endpoint, before you can
  // call other operations such as: list, get, import, delete, etc.
  const clientOptions = {apiEndpoint: 'eu-automl.googleapis.com'};

  // Instantiates a client
  const client = new automl.AutoMlClient(clientOptions);

  // A resource that represents Google Cloud Platform location.
  const projectLocation = client.locationPath(projectId, 'eu');
  // [END automl_set_endpoint]
  console.log(projectLocation);

  // Grabs the list of datasets in a given project location.
  // Note: create a dataset in `eu` before calling `listDatasets`.
  const responses = await client.listDatasets({parent: projectLocation});

  // Prints out each dataset.
  const datasets = responses[0];
  datasets.forEach(dataset => {
    console.log(dataset);
  });
}

setEndpoint(...process.argv.slice(2)).catch(err => {
  console.error(err);
  process.exitCode = 1;
});
<commit_msg>fix(samples): remove extra print statement and "setEndpoint.js" from linkinator (#261)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

'use strict';

async function setEndpoint(projectId) {
  // [START automl_set_endpoint]
  const automl = require('@google-cloud/automl').v1beta1;

  // You must first create a dataset, using the `eu` endpoint, before you can
  // call other operations such as: list, get, import, delete, etc.
  const clientOptions = {apiEndpoint: 'eu-automl.googleapis.com'};

  // Instantiates a client
  const client = new automl.AutoMlClient(clientOptions);

  // A resource that represents Google Cloud Platform location.
  const projectLocation = client.locationPath(projectId, 'eu');
  // [END automl_set_endpoint]

  // Grabs the list of datasets in a given project location.
  // Note: create a dataset in `eu` before calling `listDatasets`.
  const responses = await client.listDatasets({parent: projectLocation});

  // Prints out each dataset.
  const datasets = responses[0];
  datasets.forEach(dataset => {
    console.log(dataset);
  });
}

setEndpoint(...process.argv.slice(2)).catch(err => {
  console.error(err);
  process.exitCode = 1;
});
<|endoftext|>


<commit_before>const _ = require('lodash/fp')
const mem = require('mem')
const hkdf = require('futoin-hkdf')

const configManager = require('./config-manager')
const pify = require('pify')
const fs = pify(require('fs'))

const mnemonicHelpers = require('./mnemonic-helpers')
const options = require('./options')
const ph = require('./plugin-helper')
const layer2 = require('./layer2')

const FETCH_INTERVAL = 5000
const INSUFFICIENT_FUNDS_CODE = 570
const INSUFFICIENT_FUNDS_NAME = 'InsufficientFunds'
const ZERO_CONF_EXPIRATION = 60000

function httpError (msg, code) {
  const err = new Error(msg)
  err.name = 'HTTPError'
  err.code = code || 500

  return err
}

function computeSeed (masterSeed) {
  return hkdf(masterSeed, 32, {salt: 'lamassu-server-salt', info: 'wallet-seed'})
}

function fetchWallet (settings, cryptoCode) {
  return fs.readFile(options.mnemonicPath, 'utf8')
    .then(mnemonic => {
      const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
      const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
      const wallet = ph.load(ph.WALLET, plugin)
      const rawAccount = settings.accounts[plugin]
      const account = _.set('seed', computeSeed(masterSeed), rawAccount)
      if (_.isFunction(wallet.run)) wallet.run(account)

      return { wallet, account }
    })
}

const lastBalance = {}

function _balance (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.balance(r.account, cryptoCode))
    .then(balance => ({ balance, timestamp: Date.now() }))
    .then(r => {
      lastBalance[cryptoCode] = r
      return r
    })
    .catch(err => {
      console.error(err)
      return lastBalance[cryptoCode]
    })
}

function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
        .then(res => {
          mem.clear(module.exports.balance)
          return res
        })
    })
    .catch(err => {
      if (err.name === INSUFFICIENT_FUNDS_NAME) {
        throw httpError(INSUFFICIENT_FUNDS_NAME, INSUFFICIENT_FUNDS_CODE)
      }

      throw err
    })
}

function newAddress (settings, info) {
  const walletAddressPromise = fetchWallet(settings, info.cryptoCode)
    .then(r => r.wallet.newAddress(r.account, info))

  return Promise.all([
    walletAddressPromise,
    layer2.newAddress(settings, info)
  ])
    .then(([toAddress, layer2Address]) => ({
      toAddress,
      layer2Address
    }))
}

function newFunding (settings, cryptoCode, address) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      const wallet = r.wallet
      const account = r.account

      return wallet.newFunding(account, cryptoCode)
    })
}

function mergeStatus (a, b) {
  if (!a) return b
  if (!b) return a

  return { receivedCryptoAtoms: a.receivedCryptoAtoms, status: mergeStatusMode(a.status, b.status) }
}

function mergeStatusMode (a, b) {
  const cleared = ['authorized', 'confirmed', 'instant']
  if (_.includes(a, cleared)) return a
  if (_.includes(b, cleared)) return b

  if (a === 'published') return a
  if (b === 'published') return b

  if (a === 'rejected') return a
  if (b === 'rejected') return b

  return 'notSeen'
}

function getWalletStatus (settings, tx) {
  const fudgeFactorEnabled = configManager.unscoped(settings.config).fudgeFactor
  const fudgeFactor = fudgeFactorEnabled ? 10 : 0

  const walletStatusPromise = fetchWallet(settings, tx.cryptoCode)
    .then(r => r.wallet.getStatus(r.account, tx.toAddress, tx.cryptoAtoms.minus(fudgeFactor), tx.cryptoCode))

  return Promise.all([
    walletStatusPromise,
    layer2.getStatus(settings, tx)
  ])
    .then(([walletStatus, layer2Status]) => {
      return mergeStatus(walletStatus, layer2Status)
    })
}

function authorizeZeroConf (settings, tx, machineId) {
  const cryptoConfig = configManager.cryptoScoped(tx.cryptoCode, settings.config)
  const machineConfig = configManager.machineScoped(machineId, settings.config)
  const plugin = cryptoConfig.zeroConf
  const zeroConfLimit = machineConfig.zeroConfLimit

  if (!_.isObject(tx.fiat)) {
    return Promise.reject(new Error('tx.fiat is undefined!'))
  }

  if (plugin === 'no-zero-conf' || tx.fiat.gt(zeroConfLimit)) {
    return Promise.resolve(false)
  }

  if (plugin === 'all-zero-conf') return Promise.resolve(true)

  const zeroConf = ph.load(ph.ZERO_CONF, plugin)
  const account = settings.accounts[plugin]

  return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
}

function getStatus (settings, tx, machineId) {
  return getWalletStatus(settings, tx)
    .then((statusRec) => {
      if (statusRec.status === 'authorized') {
        return authorizeZeroConf(settings, tx, machineId)
          .then(isAuthorized => {
            const publishAge = Date.now() - tx.publishedAt

            const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION
              ? 'published'
              : 'rejected'

            const status = isAuthorized ? 'authorized' : unauthorizedStatus

            return { receivedCryptoAtoms: statusRec.receivedCryptoAtoms, status }
          })
      }

      return statusRec
    })
}

function sweep (settings, cryptoCode, hdIndex) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.sweep(r.account, cryptoCode, hdIndex))
}

function isHd (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.supportsHd)
}

function cryptoNetwork (settings, cryptoCode) {
  const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
  const wallet = ph.load(ph.WALLET, plugin)
  const account = settings.accounts[plugin]

  if (!wallet.cryptoNetwork) return Promise.resolve(false)
  return wallet.cryptoNetwork(account, cryptoCode)
}

function isStrictAddress (settings, cryptoCode, toAddress) {
  // Note: For now, only for wallets that specifically check for this.

  return fetchWallet(settings, cryptoCode)
    .then(r => {
      if (!r.wallet.isStrictAddress) return true
      return r.wallet.isStrictAddress(cryptoCode, toAddress)
    })
}

const balance = mem(_balance, {
  maxAge: FETCH_INTERVAL,
  cacheKey: (settings, cryptoCode) => cryptoCode
})

module.exports = {
  balance,
  sendCoins,
  newAddress,
  getStatus,
  isStrictAddress,
  sweep,
  isHd,
  newFunding,
  cryptoNetwork
}
<commit_msg>fix: rename fudge factor and change value<commit_after>const _ = require('lodash/fp')
const mem = require('mem')
const hkdf = require('futoin-hkdf')

const configManager = require('./config-manager')
const pify = require('pify')
const fs = pify(require('fs'))

const mnemonicHelpers = require('./mnemonic-helpers')
const options = require('./options')
const ph = require('./plugin-helper')
const layer2 = require('./layer2')

const FETCH_INTERVAL = 5000
const INSUFFICIENT_FUNDS_CODE = 570
const INSUFFICIENT_FUNDS_NAME = 'InsufficientFunds'
const ZERO_CONF_EXPIRATION = 60000

function httpError (msg, code) {
  const err = new Error(msg)
  err.name = 'HTTPError'
  err.code = code || 500

  return err
}

function computeSeed (masterSeed) {
  return hkdf(masterSeed, 32, {salt: 'lamassu-server-salt', info: 'wallet-seed'})
}

function fetchWallet (settings, cryptoCode) {
  return fs.readFile(options.mnemonicPath, 'utf8')
    .then(mnemonic => {
      const masterSeed = mnemonicHelpers.toEntropyBuffer(mnemonic)
      const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
      const wallet = ph.load(ph.WALLET, plugin)
      const rawAccount = settings.accounts[plugin]
      const account = _.set('seed', computeSeed(masterSeed), rawAccount)
      if (_.isFunction(wallet.run)) wallet.run(account)

      return { wallet, account }
    })
}

const lastBalance = {}

function _balance (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.balance(r.account, cryptoCode))
    .then(balance => ({ balance, timestamp: Date.now() }))
    .then(r => {
      lastBalance[cryptoCode] = r
      return r
    })
    .catch(err => {
      console.error(err)
      return lastBalance[cryptoCode]
    })
}

function sendCoins (settings, toAddress, cryptoAtoms, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      return r.wallet.sendCoins(r.account, toAddress, cryptoAtoms, cryptoCode)
        .then(res => {
          mem.clear(module.exports.balance)
          return res
        })
    })
    .catch(err => {
      if (err.name === INSUFFICIENT_FUNDS_NAME) {
        throw httpError(INSUFFICIENT_FUNDS_NAME, INSUFFICIENT_FUNDS_CODE)
      }

      throw err
    })
}

function newAddress (settings, info) {
  const walletAddressPromise = fetchWallet(settings, info.cryptoCode)
    .then(r => r.wallet.newAddress(r.account, info))

  return Promise.all([
    walletAddressPromise,
    layer2.newAddress(settings, info)
  ])
    .then(([toAddress, layer2Address]) => ({
      toAddress,
      layer2Address
    }))
}

function newFunding (settings, cryptoCode, address) {
  return fetchWallet(settings, cryptoCode)
    .then(r => {
      const wallet = r.wallet
      const account = r.account

      return wallet.newFunding(account, cryptoCode)
    })
}

function mergeStatus (a, b) {
  if (!a) return b
  if (!b) return a

  return { receivedCryptoAtoms: a.receivedCryptoAtoms, status: mergeStatusMode(a.status, b.status) }
}

function mergeStatusMode (a, b) {
  const cleared = ['authorized', 'confirmed', 'instant']
  if (_.includes(a, cleared)) return a
  if (_.includes(b, cleared)) return b

  if (a === 'published') return a
  if (b === 'published') return b

  if (a === 'rejected') return a
  if (b === 'rejected') return b

  return 'notSeen'
}

function getWalletStatus (settings, tx) {
  const fudgeFactorEnabled = configManager.unscoped(settings.config).fudgeFactorActive
  const fudgeFactor = fudgeFactorEnabled ? 100 : 0

  const walletStatusPromise = fetchWallet(settings, tx.cryptoCode)
    .then(r => r.wallet.getStatus(r.account, tx.toAddress, tx.cryptoAtoms.minus(fudgeFactor), tx.cryptoCode))

  return Promise.all([
    walletStatusPromise,
    layer2.getStatus(settings, tx)
  ])
    .then(([walletStatus, layer2Status]) => {
      return mergeStatus(walletStatus, layer2Status)
    })
}

function authorizeZeroConf (settings, tx, machineId) {
  const cryptoConfig = configManager.cryptoScoped(tx.cryptoCode, settings.config)
  const machineConfig = configManager.machineScoped(machineId, settings.config)
  const plugin = cryptoConfig.zeroConf
  const zeroConfLimit = machineConfig.zeroConfLimit

  if (!_.isObject(tx.fiat)) {
    return Promise.reject(new Error('tx.fiat is undefined!'))
  }

  if (plugin === 'no-zero-conf' || tx.fiat.gt(zeroConfLimit)) {
    return Promise.resolve(false)
  }

  if (plugin === 'all-zero-conf') return Promise.resolve(true)

  const zeroConf = ph.load(ph.ZERO_CONF, plugin)
  const account = settings.accounts[plugin]

  return zeroConf.authorize(account, tx.toAddress, tx.cryptoAtoms, tx.cryptoCode)
}

function getStatus (settings, tx, machineId) {
  return getWalletStatus(settings, tx)
    .then((statusRec) => {
      if (statusRec.status === 'authorized') {
        return authorizeZeroConf(settings, tx, machineId)
          .then(isAuthorized => {
            const publishAge = Date.now() - tx.publishedAt

            const unauthorizedStatus = publishAge < ZERO_CONF_EXPIRATION
              ? 'published'
              : 'rejected'

            const status = isAuthorized ? 'authorized' : unauthorizedStatus

            return { receivedCryptoAtoms: statusRec.receivedCryptoAtoms, status }
          })
      }

      return statusRec
    })
}

function sweep (settings, cryptoCode, hdIndex) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.sweep(r.account, cryptoCode, hdIndex))
}

function isHd (settings, cryptoCode) {
  return fetchWallet(settings, cryptoCode)
    .then(r => r.wallet.supportsHd)
}

function cryptoNetwork (settings, cryptoCode) {
  const plugin = configManager.cryptoScoped(cryptoCode, settings.config).wallet
  const wallet = ph.load(ph.WALLET, plugin)
  const account = settings.accounts[plugin]

  if (!wallet.cryptoNetwork) return Promise.resolve(false)
  return wallet.cryptoNetwork(account, cryptoCode)
}

function isStrictAddress (settings, cryptoCode, toAddress) {
  // Note: For now, only for wallets that specifically check for this.

  return fetchWallet(settings, cryptoCode)
    .then(r => {
      if (!r.wallet.isStrictAddress) return true
      return r.wallet.isStrictAddress(cryptoCode, toAddress)
    })
}

const balance = mem(_balance, {
  maxAge: FETCH_INTERVAL,
  cacheKey: (settings, cryptoCode) => cryptoCode
})

module.exports = {
  balance,
  sendCoins,
  newAddress,
  getStatus,
  isStrictAddress,
  sweep,
  isHd,
  newFunding,
  cryptoNetwork
}
<|endoftext|>


<commit_before>const [xml2js, request, _, utils, crypto] = [require('xml2js'), require('request'), require('lodash'), require('./utils'), require('crypto')];
// Wechat url config
const config = require('./url-config.js');

// "menu", "assistant", "user", "qrcode", "template"
const _defaultModules = ["user", "qrcode", "template"];
const _defaultEvents = ["subscribe", "unsubscribe", "scan", "report", "click", "view", "text", "image", "voice", "video", "shortvideo", "location", "link", "error", "all"];
const Wechat = function (options) {
    "use strict";
    if (!(this instanceof Wechat)) {
        return new Wechat(options);
    }
    this.appId = options.appId;
    this.appSecret = options.appSecret;
    this.token = options.appToken;
    this.encodingAESKey = options.encodingAESKey;
    this.appAccount = options.appAccount || options.appId;
    this.redis = options.redis;
    this.useEncrypt = _.isBoolean(options.encrypt) ? options.encrypt : false;
    this.tokenKey = "wechat.access_token:" + this.appId; // token 存储key
    this.jsTicketKey = "wechat.js_ticket:" + this.appId; //js Ticket
    var self = this;
    // 载入module
    (_defaultModules.concat(Array.isArray(options.modules) ? options.modules : [])).sort().filter((item, index, arr) => {
        if (index == 0)
            return true;
        return arr[index - 1] != item;
    }).forEach((clazz) => { //load clazz
        try {
            self[clazz] = require("./modules/" + clazz)(self);
        } catch (err) {// skip
            console.error(`load [$clazz] module error:`, err);
        }
    });
    this.handlers = { all: { invoke: function (msg) { return "" }, hold: false } };
};

Wechat.prototype.decrypt = function (msg, msgType) {
    try {
        let _encodingAESKey = new Buffer(this.encodingAESKey + "=", 'base64'); // 密钥base64解码
        let decipher = crypto.Decipheriv('aes-256-cbc', _encodingAESKey, _encodingAESKey.slice(0, 16)); // 创建aes-256-cbc 解密
        decipher.setAutoPadding(false); // 取消自动填充
        return decipher.update(msg, msgType, 'utf8') + decipher.final('utf8'); // 解密数据
    } catch (err) {
        console.error("数据解密失败", err);
        return msg;
    }
};
Wechat.prototype.htonl = function (n) { // 主机子节序 to 网络子节序
    var buf = new Buffer(4);
    buf[0] = (n & 0xFF000000) >> 24;
    buf[1] = (n & 0x00FF0000) >> 16;
    buf[2] = (n & 0x0000FF00) >> 8;
    buf[3] = (n & 0x000000FF) >> 0;
    return buf;
};
Wechat.prototype.ntohl = function (buf) { // 网络子节序 解析
    if (buf && buf.length) {
        var sourceNumber = 0;
        for (var i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= buf[i] & 0xff;
        }
        return sourceNumber;
    } else {
        return 0;
    }
};
Wechat.prototype.padding = function (n) { // 计算padding
    var len = n % 32;
    if (len == 0) {
        len = 32;
    } else {
        len = 32 - len;
    }
    var buf = new Buffer(len);
    for (var i = 0; i < len; i++) {
        buf[i] = len;
    }
    return buf;
};
Wechat.prototype.formatDecryptMsg = function (msg, charset, output) {
    charset = charset || "utf-8";
    output = output || "utf-8";
    var self = this;
    var buf = new Buffer(msg, charset); // string to buf
    var paddingLength = msg.charCodeAt(msg.length - 1); // 获取padding块大小
    var netBuf = buf.slice(16, 20); // 网络子节序
    buf = buf.slice(20, buf.length - paddingLength); // 消息体 ＋  微信ID
    var msgLength = this.ntohl(netBuf); // 通过网络子节序，计算消息长度
    var wechatId = buf.slice(msgLength, buf.length).toString(output); // 获取微信ID
    var msg = buf.slice(0, msgLength).toString(output); // 截取微信消息长度
    return {
        msg: msg,
        length: msgLength,
        wechatId: wechatId
    };
};
Wechat.prototype.encrypt = function (msg) {
    try {
        var self = this,
            msgBuf = new Buffer(msg, "utf-8"), // msg to buffer
            msgBufLength = msgBuf.length, // msg length
            preBuf = self.randomPrefix(16), // 16位随机串
            netBuf = self.htonl(msgBufLength), // 网络子节序
            appBuf = new Buffer(self.appId, "utf-8"), // 微信号ID
            appBufLength = appBuf.length, // 微信号ID 长度
            paddingBuf = self.padding(20 + msgBufLength + appBufLength); // pading 块
        let _encodingAESKey = new Buffer(this.encodingAESKey + "=", 'base64'); // 密钥base64解码
        let cipher = crypto.createCipheriv('aes-256-cbc', _encodingAESKey, _encodingAESKey.slice(0, 16)); // 创建aes-256-cbc 解密
        cipher.setAutoPadding(false); // 取消自动填充
        return cipher.update(Buffer.concat([preBuf, netBuf, msgBuf, appBuf, paddingBuf]), "binary", 'base64') + cipher.final('base64'); // 解密数据
    } catch (err) {
        console.error("数据加密失败", err);
        return msg;
    }
};

Wechat.prototype.randomPrefix = function (n) {
    var _str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
    var _tmp = [];
    for (var i = 0; i < n; i++) {
        _tmp.push(_str.charAt(Math.floor(Math.random() * _str.length)));
    }
    return new Buffer(_tmp);
};

Wechat.prototype.encryptMsg = function (msg) {
    let msg_encrypt = this.encrypt(new Buffer(this.toXML(msg), "utf-8"));
    let timestamp = Math.floor(Date.now() / 1000);
    let nonce = (Math.random() * 100000).toString(16).replace('\.', '');
    let msg_signature = crypto.createHash('sha1').update([this.appToken, timestamp, nonce, msg_encrypt].sort().join('')).digest('hex');
    return '<xml><Encrypt><![CDATA[' + msg_encrypt + ']]></Encrypt><MsgSignature><![CDATA[' + msg_signature + ']]></MsgSignature><TimeStamp>' + timestamp + '</TimeStamp><Nonce><![CDATA[' + nonce + ']]></Nonce></xml>';
};

Wechat.prototype.handler = function (req, res) {
    "use strict";
    let _query = req.query;
    let _signature = crypto.createHash('sha1').update([this.token, _query.timestamp, _query.nonce].sort().join('')).digest('hex');
    if (_signature != _query.signature) // check error
        return res.status(200).end('');
    var self = this;
    if (req.method.toUpperCase() !== "GET") {
        utils.streamHelper(req, (err, buf) => {
            if (err)
                return self.dealMsg('error', { req: req, res: res, msg: err });
            if (this.useEncrypt) { // encrypt msg
                xml2js.parseString(buf.toString('utf-8'), (err2, xml) => {
                    if (err2)
                        return self.dealMsg('error', { req: req, res: res, msg: err2 });
                    var formatMsg = self.formatDecryptMsg(self.decrypt(xml.xml.Encrypt[0], 'base64'));
                    self.parseData({
                        req: req,
                        res: res,
                        xml: formatMsg.msg // 应用消息
                    });
                    return;
                });
            } else {
                self.parseData({
                    req: req,
                    res: res,
                    xml: buf
                });
            }
            return;
        });
        return;
    } else {
        return res.status(200).end(_query.echostr);
    }
};

Wechat.prototype.parseData = function (data) {
    "use strict";
    var parser = new xml2js.Parser({ explicitArray: false, trim: true });
    // 解析xml
    parser.parseString(data.xml, (err, result) => {
        let msgType = "error";
        if (err) {
            data.msg = err;
        } else {
            data.msg = result.xml;
            msgType = result.xml.MsgType.toLowerCase() === "event" ? (result.xml.Event.toLowerCase() === "location" ? "report" : result.xml.Event.toLowerCase()) : result.xml.MsgType.toLowerCase();
        }
        this.dealMsg(msgType.toLowerCase(), data);
        return;
    });
};

// 处理消息
Wechat.prototype.dealMsg = function (type, data) {
    let self = this;
    let __handler = this.handlers[type] || this.handlers.all;
    if (!_.isObject(__handler) || !_.isFunction(__handler.invoke))
        __handler = { invoke: function () { return ""; }, hold: false };
    if (__handler.hold) { // keep weeting
        __handler.invoke(data.msg, (msg) => {
            console.log("done msg ==>",JSON.stringify(msg));
            if (_.isPlainObject(msg))
                return self.send(data.req, data.res, _.extend({
                    ToUserName: data.msg.FromUserName,
                    FromUserName: data.msg.ToUserName
                }, msg));
            console.log("done msg ==> empty");
            return self.send(data.req, data.res, "");
        });
    } else {
        self.send(data.req, data.res, "");
        __handler.invoke(data.msg); // 处理消息
    }
};

//将 js 组装成 xml
Wechat.prototype.toXML = function (data) {
    //自动检测 MsgType
    let _pkg = _.clone(data);
    _pkg.CreateTime = Math.floor(Date.now() / 1000);
    let _xml = new xml2js.Builder({ "rootName": "xml" }).buildObject(_pkg);
    return _xml;
};
Wechat.prototype.send = function (req, res, msg) {
    if (msg == "") {
        res.status(200).send(msg);
    } else {
        if (this.useEncrypt) {
            msg = this.encryptMsg(msg);
        } else {
            msg = this.toXML(msg);
        }
        res.type("xml").status(200).send(msg);
    }
};
// 自动刷新Token并存储到指定位置
Wechat.prototype._flushToken = function (callback) {
    if (!_.isFunction(callback))
        callback = dprint;
    request(config.token.access_token.format(this.appId, this.appSecret), (err, res) => {
        var _data = JSON.parse(res.body);
        if (_data && _data.access_token) {
            callback(_data.access_token);
        } else {
            callback(err);
        }
    });
};
// 自动刷新Token并存储到指定位置
Wechat.prototype._flushTicket = function (callback) {
    if (!_.isFunction(callback))
        callback = dprint;
    this.getToken((err, accessToken) => {
        if (err) return callback(err);
        request(config.token.js_ticket.format(accessToken), (err, res) => {
            if (err) return callback(err);
            var _data = JSON.parse(res.body);
            if (_data && _data.ticket) {
                callback(_data.ticket);
            }
        });
    });
};

Wechat.prototype.getToken = function (cb) {
    if (!_.isFunction(cb))
        cb = dprint;
    var self = this;
    self.redis.incrby(self.tokenKey + ":flush", 2, (err, flush) => {
        if (err)
            return cb(err);
        if (flush == 2) { // 获得刷新权，刷新token
            self.redis.del(self.tokenKey + ":token", dprint); // 删除已过期token
            self._flushToken((token) => {
                self.redis.multi()
                    .set(self.tokenKey + ":token", token) // 设置access token
                    .set(self.tokenKey + ":flush", 1) // 设置key 为 1
                    .expire(self.tokenKey + ":flush", 7000) // 设置key 过期时间为7000s
                    .exec(dprint);
                cb(null, token);
            });
        } else if (flush % 2 == 1) { // token 有效，直接获取
            self.redis.get(self.tokenKey + ":token", (err, token) => {
                if (err) {
                    return cb(err);
                }
                if (token) {
                    return cb(null, token); // access token 存在返回token
                }
                cb(new Error("获取access token失败"));
            });
        } else if (flush != 2 && flush % 2 == 0) { // 当前正在刷新数据，等待刷新
            setTimeout(() => { // 200ms 后再获取access token
                self.getToken(cb);
            }, 100);
        }
    });
};
Wechat.prototype.getTicket = function (cb) {
    if (!_.isFunction(cb))
        cb = dprint;
    var self = this;
    self.redis.incrby(self.jsTicketKey + ":flush", 2, (err, flush) => {
        if (err)
            return cb(err);
        if (flush == 2) { // 获得刷新权，刷新token
            self.redis.del(self.jsTicketKey + ":token", dprint); // 删除已过期token
            self._flushTicket((token) => {
                self.redis.multi()
                    .set(self.jsTicketKey + ":token", token) // 设置access token
                    .set(self.jsTicketKey + ":flush", 1) // 设置key 为 1
                    .expire(self.jsTicketKey + ":flush", 7000) // 设置key 过期时间为7000s
                    .exec(dprint);
                cb(null, token);
            });
        } else if (flush % 2 == 1) { // token 有效，直接获取
            self.redis.get(self.jsTicketKey + ":token", (err, token) => {
                if (err) {
                    return cb(err);
                }
                if (token) {
                    return cb(null, token); // access token 存在返回token
                }
                cb(new Error("获取JS ticket失败"));
            });
        } else if (flush != 2 && flush % 2 == 0) { // 当前正在刷新数据，等待刷新
            setTimeout(() => { // 200ms 后再获取access token
                self.getTicket(cb);
            }, 100);
        }
    });
};

// 处理事件保存
_defaultEvents.forEach((event) => {
    Wechat.prototype[event] = function (func, hold) {
        this.handlers[event] = { invoke: func, hold: _.isBoolean(hold) ? hold : true };
        return this;
    };
});
var dprint = function (err) {
    if (err) {
        return console.error(err);
    }
};

module.exports = Wechat;<commit_msg>1.加密修改<commit_after>const [xml2js, request, _, utils, crypto] = [require('xml2js'), require('request'), require('lodash'), require('./utils'), require('crypto')];
// Wechat url config
const config = require('./url-config.js');

// "menu", "assistant", "user", "qrcode", "template"
const _defaultModules = ["user", "qrcode", "template"];
const _defaultEvents = ["subscribe", "unsubscribe", "scan", "report", "click", "view", "text", "image", "voice", "video", "shortvideo", "location", "link", "error", "all"];
const Wechat = function (options) {
    "use strict";
    if (!(this instanceof Wechat)) {
        return new Wechat(options);
    }
    this.appId = options.appId;
    this.appSecret = options.appSecret;
    this.appToken = options.appToken;
    this.encodingAESKey = options.encodingAESKey;
    this.appAccount = options.appAccount || options.appId;
    this.redis = options.redis;
    this.useEncrypt = _.isBoolean(options.encrypt) ? options.encrypt : false;
    this.tokenKey = "wechat.access_token:" + this.appId; // token 存储key
    this.jsTicketKey = "wechat.js_ticket:" + this.appId; //js Ticket
    var self = this;
    // 载入module
    (_defaultModules.concat(Array.isArray(options.modules) ? options.modules : [])).sort().filter((item, index, arr) => {
        if (index == 0)
            return true;
        return arr[index - 1] != item;
    }).forEach((clazz) => { //load clazz
        try {
            self[clazz] = require("./modules/" + clazz)(self);
        } catch (err) {// skip
            console.error(`load [$clazz] module error:`, err);
        }
    });
    this.handlers = { all: { invoke: function (msg) { return "" }, hold: false } };
};

Wechat.prototype.decrypt = function (msg, msgType) {
    try {
        let _encodingAESKey = new Buffer(this.encodingAESKey + "=", 'base64'); // 密钥base64解码
        let decipher = crypto.Decipheriv('aes-256-cbc', _encodingAESKey, _encodingAESKey.slice(0, 16)); // 创建aes-256-cbc 解密
        decipher.setAutoPadding(false); // 取消自动填充
        return decipher.update(msg, msgType, 'utf8') + decipher.final('utf8'); // 解密数据
    } catch (err) {
        console.error("数据解密失败", err);
        return msg;
    }
};
Wechat.prototype.htonl = function (n) { // 主机子节序 to 网络子节序
    var buf = new Buffer(4);
    buf[0] = (n & 0xFF000000) >> 24;
    buf[1] = (n & 0x00FF0000) >> 16;
    buf[2] = (n & 0x0000FF00) >> 8;
    buf[3] = (n & 0x000000FF) >> 0;
    return buf;
};
Wechat.prototype.ntohl = function (buf) { // 网络子节序 解析
    if (buf && buf.length) {
        var sourceNumber = 0;
        for (var i = 0; i < 4; i++) {
            sourceNumber <<= 8;
            sourceNumber |= buf[i] & 0xff;
        }
        return sourceNumber;
    } else {
        return 0;
    }
};
Wechat.prototype.padding = function (n) { // 计算padding
    var len = n % 32;
    if (len == 0) {
        len = 32;
    } else {
        len = 32 - len;
    }
    var buf = new Buffer(len);
    for (var i = 0; i < len; i++) {
        buf[i] = len;
    }
    return buf;
};
Wechat.prototype.formatDecryptMsg = function (msg, charset, output) {
    charset = charset || "utf-8";
    output = output || "utf-8";
    var self = this;
    var buf = new Buffer(msg, charset); // string to buf
    var paddingLength = msg.charCodeAt(msg.length - 1); // 获取padding块大小
    var netBuf = buf.slice(16, 20); // 网络子节序
    buf = buf.slice(20, buf.length - paddingLength); // 消息体 ＋  微信ID
    var msgLength = this.ntohl(netBuf); // 通过网络子节序，计算消息长度
    var wechatId = buf.slice(msgLength, buf.length).toString(output); // 获取微信ID
    var msg = buf.slice(0, msgLength).toString(output); // 截取微信消息长度
    return {
        msg: msg,
        length: msgLength,
        wechatId: wechatId
    };
};
Wechat.prototype.encrypt = function (msg) {
    try {
        var self = this,
            msgBuf = new Buffer(msg, "utf-8"), // msg to buffer
            msgBufLength = msgBuf.length, // msg length
            preBuf = self.randomPrefix(16), // 16位随机串
            netBuf = self.htonl(msgBufLength), // 网络子节序
            appBuf = new Buffer(self.appId, "utf-8"), // 微信号ID
            appBufLength = appBuf.length, // 微信号ID 长度
            paddingBuf = self.padding(20 + msgBufLength + appBufLength); // pading 块
        let _encodingAESKey = new Buffer(this.encodingAESKey + "=", 'base64'); // 密钥base64解码
        let cipher = crypto.createCipheriv('aes-256-cbc', _encodingAESKey, _encodingAESKey.slice(0, 16)); // 创建aes-256-cbc 解密
        cipher.setAutoPadding(false); // 取消自动填充
        return cipher.update(Buffer.concat([preBuf, netBuf, msgBuf, appBuf, paddingBuf]), "binary", 'base64') + cipher.final('base64'); // 解密数据
    } catch (err) {
        console.error("数据加密失败", err);
        return msg;
    }
};

Wechat.prototype.randomPrefix = function (n) {
    var _str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
    var _tmp = [];
    for (var i = 0; i < n; i++) {
        _tmp.push(_str.charAt(Math.floor(Math.random() * _str.length)));
    }
    return new Buffer(_tmp);
};

Wechat.prototype.encryptMsg = function (msg) {
    let msg_encrypt = this.encrypt(new Buffer(this.toXML(msg), "utf-8"));
    let timestamp = Math.floor(Date.now() / 1000);
    let nonce = (Math.random() * 100000).toString(16).replace('\.', '');
    let msg_signature = crypto.createHash('sha1').update([this.appToken, timestamp, nonce, msg_encrypt].sort().join('')).digest('hex');
    return '<xml><Encrypt><![CDATA[' + msg_encrypt + ']]></Encrypt><MsgSignature><![CDATA[' + msg_signature + ']]></MsgSignature><TimeStamp>' + timestamp + '</TimeStamp><Nonce><![CDATA[' + nonce + ']]></Nonce></xml>';
};

Wechat.prototype.handler = function (req, res) {
    "use strict";
    let _query = req.query;
    let _signature = crypto.createHash('sha1').update([this.token, _query.timestamp, _query.nonce].sort().join('')).digest('hex');
    if (_signature != _query.signature) // check error
        return res.status(200).end('');
    var self = this;
    if (req.method.toUpperCase() !== "GET") {
        utils.streamHelper(req, (err, buf) => {
            if (err)
                return self.dealMsg('error', { req: req, res: res, msg: err });
            if (this.useEncrypt) { // encrypt msg
                xml2js.parseString(buf.toString('utf-8'), (err2, xml) => {
                    if (err2)
                        return self.dealMsg('error', { req: req, res: res, msg: err2 });
                    var formatMsg = self.formatDecryptMsg(self.decrypt(xml.xml.Encrypt[0], 'base64'));
                    self.parseData({
                        req: req,
                        res: res,
                        xml: formatMsg.msg // 应用消息
                    });
                    return;
                });
            } else {
                self.parseData({
                    req: req,
                    res: res,
                    xml: buf
                });
            }
            return;
        });
        return;
    } else {
        return res.status(200).end(_query.echostr);
    }
};

Wechat.prototype.parseData = function (data) {
    "use strict";
    var parser = new xml2js.Parser({ explicitArray: false, trim: true });
    // 解析xml
    parser.parseString(data.xml, (err, result) => {
        let msgType = "error";
        if (err) {
            data.msg = err;
        } else {
            data.msg = result.xml;
            msgType = result.xml.MsgType.toLowerCase() === "event" ? (result.xml.Event.toLowerCase() === "location" ? "report" : result.xml.Event.toLowerCase()) : result.xml.MsgType.toLowerCase();
        }
        this.dealMsg(msgType.toLowerCase(), data);
        return;
    });
};

// 处理消息
Wechat.prototype.dealMsg = function (type, data) {
    let self = this;
    let __handler = this.handlers[type] || this.handlers.all;
    if (!_.isObject(__handler) || !_.isFunction(__handler.invoke))
        __handler = { invoke: function () { return ""; }, hold: false };
    if (__handler.hold) { // keep weeting
        __handler.invoke(data.msg, (msg) => {
            console.log("done msg ==>",JSON.stringify(msg));
            if (_.isPlainObject(msg))
                return self.send(data.req, data.res, _.extend({
                    ToUserName: data.msg.FromUserName,
                    FromUserName: data.msg.ToUserName
                }, msg));
            console.log("done msg ==> empty");
            return self.send(data.req, data.res, "");
        });
    } else {
        self.send(data.req, data.res, "");
        __handler.invoke(data.msg); // 处理消息
    }
};

//将 js 组装成 xml
Wechat.prototype.toXML = function (data) {
    //自动检测 MsgType
    let _pkg = _.clone(data);
    _pkg.CreateTime = Math.floor(Date.now() / 1000);
    let _xml = new xml2js.Builder({ "rootName": "xml" }).buildObject(_pkg);
    return _xml;
};
Wechat.prototype.send = function (req, res, msg) {
    if (msg == "") {
        res.status(200).send(msg);
    } else {
        if (this.useEncrypt) {
            msg = this.encryptMsg(msg);
        } else {
            msg = this.toXML(msg);
        }
        res.type("xml").status(200).send(msg);
    }
};
// 自动刷新Token并存储到指定位置
Wechat.prototype._flushToken = function (callback) {
    if (!_.isFunction(callback))
        callback = dprint;
    request(config.token.access_token.format(this.appId, this.appSecret), (err, res) => {
        var _data = JSON.parse(res.body);
        if (_data && _data.access_token) {
            callback(_data.access_token);
        } else {
            callback(err);
        }
    });
};
// 自动刷新Token并存储到指定位置
Wechat.prototype._flushTicket = function (callback) {
    if (!_.isFunction(callback))
        callback = dprint;
    this.getToken((err, accessToken) => {
        if (err) return callback(err);
        request(config.token.js_ticket.format(accessToken), (err, res) => {
            if (err) return callback(err);
            var _data = JSON.parse(res.body);
            if (_data && _data.ticket) {
                callback(_data.ticket);
            }
        });
    });
};

Wechat.prototype.getToken = function (cb) {
    if (!_.isFunction(cb))
        cb = dprint;
    var self = this;
    self.redis.incrby(self.tokenKey + ":flush", 2, (err, flush) => {
        if (err)
            return cb(err);
        if (flush == 2) { // 获得刷新权，刷新token
            self.redis.del(self.tokenKey + ":token", dprint); // 删除已过期token
            self._flushToken((token) => {
                self.redis.multi()
                    .set(self.tokenKey + ":token", token) // 设置access token
                    .set(self.tokenKey + ":flush", 1) // 设置key 为 1
                    .expire(self.tokenKey + ":flush", 7000) // 设置key 过期时间为7000s
                    .exec(dprint);
                cb(null, token);
            });
        } else if (flush % 2 == 1) { // token 有效，直接获取
            self.redis.get(self.tokenKey + ":token", (err, token) => {
                if (err) {
                    return cb(err);
                }
                if (token) {
                    return cb(null, token); // access token 存在返回token
                }
                cb(new Error("获取access token失败"));
            });
        } else if (flush != 2 && flush % 2 == 0) { // 当前正在刷新数据，等待刷新
            setTimeout(() => { // 200ms 后再获取access token
                self.getToken(cb);
            }, 100);
        }
    });
};
Wechat.prototype.getTicket = function (cb) {
    if (!_.isFunction(cb))
        cb = dprint;
    var self = this;
    self.redis.incrby(self.jsTicketKey + ":flush", 2, (err, flush) => {
        if (err)
            return cb(err);
        if (flush == 2) { // 获得刷新权，刷新token
            self.redis.del(self.jsTicketKey + ":token", dprint); // 删除已过期token
            self._flushTicket((token) => {
                self.redis.multi()
                    .set(self.jsTicketKey + ":token", token) // 设置access token
                    .set(self.jsTicketKey + ":flush", 1) // 设置key 为 1
                    .expire(self.jsTicketKey + ":flush", 7000) // 设置key 过期时间为7000s
                    .exec(dprint);
                cb(null, token);
            });
        } else if (flush % 2 == 1) { // token 有效，直接获取
            self.redis.get(self.jsTicketKey + ":token", (err, token) => {
                if (err) {
                    return cb(err);
                }
                if (token) {
                    return cb(null, token); // access token 存在返回token
                }
                cb(new Error("获取JS ticket失败"));
            });
        } else if (flush != 2 && flush % 2 == 0) { // 当前正在刷新数据，等待刷新
            setTimeout(() => { // 200ms 后再获取access token
                self.getTicket(cb);
            }, 100);
        }
    });
};

// 处理事件保存
_defaultEvents.forEach((event) => {
    Wechat.prototype[event] = function (func, hold) {
        this.handlers[event] = { invoke: func, hold: _.isBoolean(hold) ? hold : true };
        return this;
    };
});
var dprint = function (err) {
    if (err) {
        return console.error(err);
    }
};

module.exports = Wechat;<|endoftext|>


<commit_before>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

/**
* Compute a maximum absolute value incrementally.
*
* @module @stdlib/stats/incr/maxabs
*
* @example
* var incrmaxabs = require( '@stdlib/stats/incr/maxabs' );
*
* var accumulator = incrmaxabs();
*
* var max = accumulator();
* // returns null
*
* max = accumulator( 3.14 );
* // returns 3.14
*
* max = accumulator( -5.0 );
* // returns -5.0
*
* max = accumulator( 10.1 );
* // returns 10.1
*
* max = accumulator();
* // returns 10.1
*/

// MODULES //

var incrmaxabs = require( './main.js' );

// EXPORTS //

module.exports = incrmaxabs;
<commit_msg>Fix sign<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

/**
* Compute a maximum absolute value incrementally.
*
* @module @stdlib/stats/incr/maxabs
*
* @example
* var incrmaxabs = require( '@stdlib/stats/incr/maxabs' );
*
* var accumulator = incrmaxabs();
*
* var max = accumulator();
* // returns null
*
* max = accumulator( 3.14 );
* // returns 3.14
*
* max = accumulator( -5.0 );
* // returns 5.0
*
* max = accumulator( 10.1 );
* // returns 10.1
*
* max = accumulator();
* // returns 10.1
*/

// MODULES //

var incrmaxabs = require( './main.js' );

// EXPORTS //

module.exports = incrmaxabs;
<|endoftext|>


<commit_before>var Router = Backbone.Router.extend({
	routes: {
		"": "clear",
		"feed/:id": "viewFeed",
		"folder/:id": "viewFolder",
		"item": "viewAllItems"
	},
	initialize: function() {
		this.itemList = new Backbone.Marionette.Region({
			el: '#item-list'
		});
	},
	clear: function() {
		this.itemList.close();

		if(currentSelection !== null) {
			currentSelection.set('active', false);
			currentSelection = null;
		}
		window.scrollTo(0,0);

		$('.mobile-item-button').addClass('invisible');
		$('#item-list').addClass('hidden-phone');
		$('.feed-list').removeClass('hidden-phone');
	},
	updateItemList : function(model) {
		items = new ItemListView({collection: model.items});
		model.items.fetch({reset: true, reset_pagination: true});

		this.itemList.show(items);

		if(currentSelection !== null) {
			currentSelection.set('active', false);
		}
		model.set('active', true);
		currentSelection = model;
		window.scrollTo(0,0);
		$('#item-list').removeClass('hidden-phone');
		$('.feed-list').addClass('hidden-phone');
		$('.mobile-item-button').removeClass('invisible');
		$('#item-list').focus();
	},
	viewFeed: function(id) {
		var model = null;
		folders.each(function(folder) {
			if(folder.feeds.get(id)) {
				model = folder.feeds.get(id);
			}
		});

		this.updateItemList(model);
	},
	viewFolder: function(id) {
		var model = folders.get(id);

		this.updateItemList(model);
	},
	viewAllItems: function() {
		var model = folderList.allItemsFolder;
		this.updateItemList(model);
	},
	goToModel: function(model) {
		if(model instanceof Folder) {
			this.navigate('folder/' + model.id, {trigger: true});
		}
		else if(model instanceof Feed) {
			this.navigate('feed/' + model.id, {trigger: true});
		}
		else {
			this.navigate('item', {trigger: true});
		}
    }
});
<commit_msg>Fix the itemlist's behavior on feed change<commit_after>var Router = Backbone.Router.extend({
	routes: {
		"": "clear",
		"feed/:id": "viewFeed",
		"folder/:id": "viewFolder",
		"item": "viewAllItems"
	},
	initialize: function() {
		this.itemList = new Backbone.Marionette.Region({
			el: '#item-list'
		});
	},
	clear: function() {
		this.itemList.close();

		if(currentSelection !== null) {
			currentSelection.set('active', false);
			currentSelection = null;
		}
		window.scrollTo(0,0);

		$('.mobile-item-button').addClass('invisible');
		$('#item-list').addClass('hidden-phone');
		$('.feed-list').removeClass('hidden-phone');
	},
	updateItemList : function(model) {
		var that = this;
		items = new ItemListView({collection: model.items});
		model.items.fetch({reset: true, reset_pagination: true, success: function() {
			that.itemList.show(items);
		}});

		if(currentSelection !== null) {
			currentSelection.set('active', false);
		}
		model.set('active', true);
		currentSelection = model;
		window.scrollTo(0,0);
		$('#item-list').removeClass('hidden-phone');
		$('.feed-list').addClass('hidden-phone');
		$('.mobile-item-button').removeClass('invisible');
		$('#item-list').focus();
	},
	viewFeed: function(id) {
		var model = null;
		folders.each(function(folder) {
			if(folder.feeds.get(id)) {
				model = folder.feeds.get(id);
			}
		});

		this.updateItemList(model);
	},
	viewFolder: function(id) {
		var model = folders.get(id);

		this.updateItemList(model);
	},
	viewAllItems: function() {
		var model = folderList.allItemsFolder;
		this.updateItemList(model);
	},
	goToModel: function(model) {
		if(model instanceof Folder) {
			this.navigate('folder/' + model.id, {trigger: true});
		}
		else if(model instanceof Feed) {
			this.navigate('feed/' + model.id, {trigger: true});
		}
		else {
			this.navigate('item', {trigger: true});
		}
    }
});
<|endoftext|>


<commit_before>var moment = require('moment'),
    Transform = require('stream').Transform,
    format = require('util').format,
    prettyjson = require('prettyjson'),
    each = require('lodash/each'),
    omit = require('lodash/omit'),
    get = require('lodash/get'),
    isArray = require('lodash/isArray'),
    isEmpty = require('lodash/isEmpty'),
    isObject = require('lodash/isObject'),
    isString = require('lodash/isString'),
    _private = {
        levelFromName: {
            10: 'trace',
            20: 'debug',
            30: 'info',
            40: 'warn',
            50: 'error',
            60: 'fatal'
        },
        colorForLevel: {
            10: 'grey',
            20: 'grey',
            30: 'cyan',
            40: 'magenta',
            50: 'red',
            60: 'inverse'
        },
        colors: {
            'bold': [1, 22],
            'italic': [3, 23],
            'underline': [4, 24],
            'inverse': [7, 27],
            'white': [37, 39],
            'grey': [90, 39],
            'black': [30, 39],
            'blue': [34, 39],
            'cyan': [36, 39],
            'green': [32, 39],
            'magenta': [35, 39],
            'red': [31, 39],
            'yellow': [33, 39]
        }
    };

function colorize(colors, value) {
    if (isArray(colors)) {
        return colors.reduce((acc, color) => colorize(color, acc), value);
    } else {
        return '\x1B[' + _private.colors[colors][0] + 'm' + value + '\x1B[' + _private.colors[colors][1] + 'm';
    }
}

function statusCode(status) {
    var color = status >= 500 ? 'red'
        : status >= 400 ? 'yellow'
        : status >= 300 ? 'cyan'
        : status >= 200 ? 'green'
        : 0 // no color

    return colorize(color, status);
}

class PrettyStream extends Transform {
    constructor(options) {
        options = options || {};
        super(options);

        this.mode = options.mode || 'short';
    }

    write(data, enc, cb) {
        // Bunyan sometimes passes things as objects. Because of this, we need to make sure
        // the data is converted to JSON
        if (isObject(data) && !(data instanceof Buffer)) {
            data = JSON.stringify(data);
        }

        super.write(data, enc, cb);
    }

    _transform(data, enc, cb) {
        if (!isString(data)) {
            data = data.toString();
        }

        // Remove trailing newline if any
        data = data.replace(/\\n$/, '');

        try {
            data = JSON.parse(data);
        } catch (err) {
            cb(err);
            // If data is not JSON we don't want to continue processing as if it is
            return;
        }

        var output = '',
            time = moment(data.time).format('YYYY-MM-DD HH:mm:ss'),
            logLevel = _private.levelFromName[data.level].toUpperCase(),
            codes = _private.colors[_private.colorForLevel[data.level]],
            bodyPretty = '';

        logLevel = '\x1B[' + codes[0] + 'm' + logLevel + '\x1B[' + codes[1] + 'm';

        if (data.req) {
            output += format('[%s] %s "%s %s" %s %s\n',
                time,
                logLevel,
                data.req.method.toUpperCase(),
                get(data, 'req.originalUrl'),
                statusCode(get(data, 'res.statusCode')),
                get(data, 'res.responseTime')
            );
        }
        else if (data.msg === undefined) {
            output += format('[%s] %s\n',
                time,
                logLevel
            );
        }
        else {
            bodyPretty += data.msg;
            output += format('[%s] %s %s\n',
               time,
               logLevel,
               bodyPretty
            );
        }

        each(omit(data, ['time', 'level', 'name', 'hostname', 'pid', 'v', 'msg']), function (value, key) {
            // we always output errors for now
            if (isObject(value) && value.message && value.stack) {
                var error = '\n';

                if (value.errorType) {
                    error += colorize(_private.colorForLevel[data.level], 'Type: ' + value.errorType) + '\n';
                }

                error += colorize(_private.colorForLevel[data.level], value.message) + '\n\n';

                if (value.context) {
                    error += colorize('white', value.context) + '\n';
                }

                if (value.help) {
                    error += colorize('yellow', value.help) + '\n';
                }

                if (value.context || value.help) {
                    error += '\n';
                }

                if (value.id) {
                    error += colorize(['white', 'bold'], 'Error ID:') + '\n';
                    error += '    ' + colorize('grey', value.id) + '\n\n';
                }

                if (value.code) {
                    error += colorize(['white', 'bold'], 'Error Code: ') + '\n';
                    error += '    ' + colorize('grey', value.code) + '\n\n';
                }

                if (value.errorDetails) {
                    let details = value.errorDetails;

                    try {
                        const jsonDetails = JSON.parse(value.errorDetails);
                        details = isArray(jsonDetails) ? jsonDetails[0] :jsonDetails;
                    } catch (err) {
                        // no need for special handling as we default to unparsed 'errorDetails'
                    }

                    const pretty = prettyjson.render(details, {
                        noColor: true
                    }, 4);

                    error += colorize(['white', 'bold'], 'Details:') + '\n';
                    error += colorize('grey', pretty) + '\n\n';
                }

                if (value.stack && !value.hideStack) {
                    error += colorize('grey', '----------------------------------------') + '\n\n';
                    error += colorize('grey', value.stack) + '\n';
                }

                output += format('%s\n', colorize(_private.colorForLevel[data.level], error));
            }
            else if (isObject(value)) {
                bodyPretty += '\n' + colorize('yellow', key.toUpperCase()) + '\n';

                var sanitized = {};

                each(value, function (innerValue, innerKey) {
                    if (!isEmpty(innerValue)) {
                        sanitized[innerKey] = innerValue;
                    }
                });

                bodyPretty += prettyjson.render(sanitized, {}) + '\n';
            } else {
                bodyPretty += prettyjson.render(value, {}) + '\n';
            }
        });

        if (this.mode !== 'short' && (bodyPretty !== data.msg)) {
            output += format('%s\n', colorize('grey', bodyPretty));
        }

        cb(null, output);
    }
}

module.exports = PrettyStream;
<commit_msg>🐛 Fix logging when statusCode is invalid<commit_after>var moment = require('moment'),
    Transform = require('stream').Transform,
    format = require('util').format,
    prettyjson = require('prettyjson'),
    each = require('lodash/each'),
    omit = require('lodash/omit'),
    get = require('lodash/get'),
    isArray = require('lodash/isArray'),
    isEmpty = require('lodash/isEmpty'),
    isObject = require('lodash/isObject'),
    isString = require('lodash/isString'),
    _private = {
        levelFromName: {
            10: 'trace',
            20: 'debug',
            30: 'info',
            40: 'warn',
            50: 'error',
            60: 'fatal'
        },
        colorForLevel: {
            10: 'grey',
            20: 'grey',
            30: 'cyan',
            40: 'magenta',
            50: 'red',
            60: 'inverse'
        },
        colors: {
            'default': [39, 39],
            'bold': [1, 22],
            'italic': [3, 23],
            'underline': [4, 24],
            'inverse': [7, 27],
            'white': [37, 39],
            'grey': [90, 39],
            'black': [30, 39],
            'blue': [34, 39],
            'cyan': [36, 39],
            'green': [32, 39],
            'magenta': [35, 39],
            'red': [31, 39],
            'yellow': [33, 39]
        }
    };

function colorize(colors, value) {
    if (isArray(colors)) {
        return colors.reduce((acc, color) => colorize(color, acc), value);
    } else {
        return '\x1B[' + _private.colors[colors][0] + 'm' + value + '\x1B[' + _private.colors[colors][1] + 'm';
    }
}

function statusCode(status) {
    var color = status >= 500 ? 'red'
        : status >= 400 ? 'yellow'
        : status >= 300 ? 'cyan'
        : status >= 200 ? 'green'
        : 'default' // no color

    return colorize(color, status);
}

class PrettyStream extends Transform {
    constructor(options) {
        options = options || {};
        super(options);

        this.mode = options.mode || 'short';
    }

    write(data, enc, cb) {
        // Bunyan sometimes passes things as objects. Because of this, we need to make sure
        // the data is converted to JSON
        if (isObject(data) && !(data instanceof Buffer)) {
            data = JSON.stringify(data);
        }

        super.write(data, enc, cb);
    }

    _transform(data, enc, cb) {
        if (!isString(data)) {
            data = data.toString();
        }

        // Remove trailing newline if any
        data = data.replace(/\\n$/, '');

        try {
            data = JSON.parse(data);
        } catch (err) {
            cb(err);
            // If data is not JSON we don't want to continue processing as if it is
            return;
        }

        var output = '',
            time = moment(data.time).format('YYYY-MM-DD HH:mm:ss'),
            logLevel = _private.levelFromName[data.level].toUpperCase(),
            codes = _private.colors[_private.colorForLevel[data.level]],
            bodyPretty = '';

        logLevel = '\x1B[' + codes[0] + 'm' + logLevel + '\x1B[' + codes[1] + 'm';

        if (data.req) {
            output += format('[%s] %s "%s %s" %s %s\n',
                time,
                logLevel,
                data.req.method.toUpperCase(),
                get(data, 'req.originalUrl'),
                statusCode(get(data, 'res.statusCode')),
                get(data, 'res.responseTime')
            );
        }
        else if (data.msg === undefined) {
            output += format('[%s] %s\n',
                time,
                logLevel
            );
        }
        else {
            bodyPretty += data.msg;
            output += format('[%s] %s %s\n',
               time,
               logLevel,
               bodyPretty
            );
        }

        each(omit(data, ['time', 'level', 'name', 'hostname', 'pid', 'v', 'msg']), function (value, key) {
            // we always output errors for now
            if (isObject(value) && value.message && value.stack) {
                var error = '\n';

                if (value.errorType) {
                    error += colorize(_private.colorForLevel[data.level], 'Type: ' + value.errorType) + '\n';
                }

                error += colorize(_private.colorForLevel[data.level], value.message) + '\n\n';

                if (value.context) {
                    error += colorize('white', value.context) + '\n';
                }

                if (value.help) {
                    error += colorize('yellow', value.help) + '\n';
                }

                if (value.context || value.help) {
                    error += '\n';
                }

                if (value.id) {
                    error += colorize(['white', 'bold'], 'Error ID:') + '\n';
                    error += '    ' + colorize('grey', value.id) + '\n\n';
                }

                if (value.code) {
                    error += colorize(['white', 'bold'], 'Error Code: ') + '\n';
                    error += '    ' + colorize('grey', value.code) + '\n\n';
                }

                if (value.errorDetails) {
                    let details = value.errorDetails;

                    try {
                        const jsonDetails = JSON.parse(value.errorDetails);
                        details = isArray(jsonDetails) ? jsonDetails[0] :jsonDetails;
                    } catch (err) {
                        // no need for special handling as we default to unparsed 'errorDetails'
                    }

                    const pretty = prettyjson.render(details, {
                        noColor: true
                    }, 4);

                    error += colorize(['white', 'bold'], 'Details:') + '\n';
                    error += colorize('grey', pretty) + '\n\n';
                }

                if (value.stack && !value.hideStack) {
                    error += colorize('grey', '----------------------------------------') + '\n\n';
                    error += colorize('grey', value.stack) + '\n';
                }

                output += format('%s\n', colorize(_private.colorForLevel[data.level], error));
            }
            else if (isObject(value)) {
                bodyPretty += '\n' + colorize('yellow', key.toUpperCase()) + '\n';

                var sanitized = {};

                each(value, function (innerValue, innerKey) {
                    if (!isEmpty(innerValue)) {
                        sanitized[innerKey] = innerValue;
                    }
                });

                bodyPretty += prettyjson.render(sanitized, {}) + '\n';
            } else {
                bodyPretty += prettyjson.render(value, {}) + '\n';
            }
        });

        if (this.mode !== 'short' && (bodyPretty !== data.msg)) {
            output += format('%s\n', colorize('grey', bodyPretty));
        }

        cb(null, output);
    }
}

module.exports = PrettyStream;
<|endoftext|>