<commit_before>
const request = require('supertest');
const app = require('../../../src/app');

beforeAll((done) => {
  app.on('ready', () => {
    done();
  });
});

//------------------------------------------------------------
//                        All Launches
//------------------------------------------------------------

test('It should return all launches', async () => {
  const response = await request(app.callback()).get('/v2/launches/all');
  expect(response.statusCode).toBe(200);
  response.body.forEach((item) => {
    expect(item).toHaveProperty('flight_number', expect.anything());
    expect(item).toHaveProperty('mission_name');
    expect(item).toHaveProperty('launch_year', expect.stringMatching(/^[0-9]{4}$/));
    expect(item).toHaveProperty('launch_date_unix');
    expect(item).toHaveProperty('launch_date_utc');
    expect(item).toHaveProperty('launch_date_local');
    expect(item).toHaveProperty('rocket.rocket_id');
    expect(item).toHaveProperty('rocket.rocket_name');
    expect(item).toHaveProperty('rocket.rocket_type');
    expect(item.rocket.first_stage.cores.length).toBeGreaterThan(0);
    item.rocket.first_stage.cores.forEach((core) => {
      expect(core).toHaveProperty('core_serial');
      expect(core).toHaveProperty('flight');
      expect(core).toHaveProperty('block');
      expect(core).toHaveProperty('reused');
      expect(core).toHaveProperty('land_success');
      expect(core).toHaveProperty('landing_type');
      expect(core).toHaveProperty('landing_vehicle');
    });
    expect(item.rocket.second_stage).toHaveProperty('block');
    expect(item.rocket.second_stage.payloads.length).toBeGreaterThan(0);
    if (item.hasOwnProperty.call('cap_serial')) {
      item.rocket.second_stage.payloads.forEach((payload) => {
        expect(payload).toHaveProperty('payload_id');
        expect(payload).toHaveProperty('reused');
        expect(payload).toHaveProperty('cap_serial');
        expect(payload.customers.length).toBeGreaterThan(0);
        expect(payload).toHaveProperty('payload_mass_kg');
        expect(payload).toHaveProperty('payload_mass_lbs');
        expect(payload).toHaveProperty('orbit');
        expect(payload).toHaveProperty('orbit_params');
        expect(payload).toHaveProperty('orbit_params.reference_system');
        expect(payload).toHaveProperty('orbit_params.regime');
        expect(payload).toHaveProperty('orbit_params.longitude');
        expect(payload).toHaveProperty('orbit_params.semi_major_axis_km');
        expect(payload).toHaveProperty('orbit_params.eccentricity');
        expect(payload).toHaveProperty('orbit_params.periapsis_km');
        expect(payload).toHaveProperty('orbit_params.apoapsis_km');
        expect(payload).toHaveProperty('orbit_params.inclination_deg');
        expect(payload).toHaveProperty('orbit_params.period_min');
        expect(payload).toHaveProperty('orbit_params.lifespan_years');
        expect(payload).toHaveProperty('mass_returned_kg');
        expect(payload).toHaveProperty('mass_returned_lbs');
        expect(payload).toHaveProperty('flight_time_sec');
        expect(payload).toHaveProperty('cargo_manifest');
      });
    } else {
      item.rocket.second_stage.payloads.forEach((payload) => {
        expect(payload).toHaveProperty('payload_id');
        expect(payload).toHaveProperty('reused');
        expect(payload.customers.length).toBeGreaterThan(0);
        expect(payload).toHaveProperty('payload_mass_kg');
        expect(payload).toHaveProperty('payload_mass_lbs');
        expect(payload).toHaveProperty('orbit');
        expect(payload).toHaveProperty('orbit_params');
        expect(payload).toHaveProperty('orbit_params.reference_system');
        expect(payload).toHaveProperty('orbit_params.regime');
        expect(payload).toHaveProperty('orbit_params.longitude');
        expect(payload).toHaveProperty('orbit_params.semi_major_axis_km');
        expect(payload).toHaveProperty('orbit_params.eccentricity');
        expect(payload).toHaveProperty('orbit_params.periapsis_km');
        expect(payload).toHaveProperty('orbit_params.apoapsis_km');
        expect(payload).toHaveProperty('orbit_params.inclination_deg');
        expect(payload).toHaveProperty('orbit_params.period_min');
        expect(payload).toHaveProperty('orbit_params.lifespan_years');
      });
    }
    expect(item).toHaveProperty('telemetry.flight_club');
    expect(item).toHaveProperty('reuse.core');
    expect(item).toHaveProperty('reuse.side_core1');
    expect(item).toHaveProperty('reuse.side_core2');
    expect(item).toHaveProperty('reuse.fairings');
    expect(item).toHaveProperty('reuse.capsule');
    expect(item).toHaveProperty('launch_site.site_id');
    expect(item).toHaveProperty('launch_site.site_name');
    expect(item).toHaveProperty('launch_site.site_name_long');
    expect(item).toHaveProperty('launch_success');
    expect(item).toHaveProperty('links');
    expect(item).toHaveProperty('details');
  });
});

//------------------------------------------------------------
//                       Latest Launch
//------------------------------------------------------------

test('It should return the latest launch', async () => {
  const response = await request(app.callback()).get('/v2/launches/latest');
  expect(response.statusCode).toBe(200);
  expect(response.body).toHaveProperty('upcoming', false);
});

//------------------------------------------------------------
//                       Next Launch
//------------------------------------------------------------

test('It should return the next launch', async () => {
  const response = await request(app.callback()).get('/v2/launches/next');
  expect(response.statusCode).toBe(200);
  expect(response.body).toHaveProperty('upcoming', true);
});

//------------------------------------------------------------
//                       Past Launches
//------------------------------------------------------------

test('It should return the all launches', async () => {
  const response = await request(app.callback()).get('/v2/launches');
  expect(response.statusCode).toBe(200);
  response.body.forEach((launch) => {
    expect(launch.upcoming).toBe(false);
  });
});

test('It should return no launches due to invalid date', async () => {
  const response = await request(app.callback()).get('/v2/launches?start=2020-25-23&end=2020-25-24');
  expect(response.statusCode).toBe(200);
  expect(response.body).toEqual([]);
});

test('It should return no launches due to invalid UTC date', async () => {
  const response = await request(app.callback()).get('/v2/launches?launch_date_utc=2011-25-05T14:48:00.000Z');
  expect(response.statusCode).toBe(200);
  expect(response.body).toEqual([]);
});

//------------------------------------------------------------
//                       Upcoming Launches
//------------------------------------------------------------

test('It should return the next launch', async () => {
  const response = await request(app.callback()).get('/v2/launches/upcoming');
  expect(response.statusCode).toBe(200);
  response.body.forEach((launch) => {
    expect(launch.upcoming).toBe(true);
  });
});
<commit_msg>upcoming launches can have unknown customer names<commit_after>
const request = require('supertest');
const app = require('../../../src/app');

beforeAll((done) => {
  app.on('ready', () => {
    done();
  });
});

//------------------------------------------------------------
//                        All Launches
//------------------------------------------------------------

test('It should return all launches', async () => {
  const response = await request(app.callback()).get('/v2/launches/all');
  expect(response.statusCode).toBe(200);
  response.body.forEach((item) => {
    expect(item).toHaveProperty('flight_number', expect.anything());
    expect(item).toHaveProperty('mission_name');
    expect(item).toHaveProperty('launch_year', expect.stringMatching(/^[0-9]{4}$/));
    expect(item).toHaveProperty('launch_date_unix');
    expect(item).toHaveProperty('launch_date_utc');
    expect(item).toHaveProperty('launch_date_local');
    expect(item).toHaveProperty('rocket.rocket_id');
    expect(item).toHaveProperty('rocket.rocket_name');
    expect(item).toHaveProperty('rocket.rocket_type');
    expect(item.rocket.first_stage.cores.length).toBeGreaterThan(0);
    item.rocket.first_stage.cores.forEach((core) => {
      expect(core).toHaveProperty('core_serial');
      expect(core).toHaveProperty('flight');
      expect(core).toHaveProperty('block');
      expect(core).toHaveProperty('reused');
      expect(core).toHaveProperty('land_success');
      expect(core).toHaveProperty('landing_type');
      expect(core).toHaveProperty('landing_vehicle');
    });
    expect(item.rocket.second_stage).toHaveProperty('block');
    expect(item.rocket.second_stage.payloads.length).toBeGreaterThan(0);
    if (item.hasOwnProperty.call('cap_serial')) {
      item.rocket.second_stage.payloads.forEach((payload) => {
        expect(payload).toHaveProperty('payload_id');
        expect(payload).toHaveProperty('reused');
        expect(payload).toHaveProperty('cap_serial');
        expect(payload.customers.length).toBeGreaterThanOrEqual(0);
        expect(payload).toHaveProperty('payload_mass_kg');
        expect(payload).toHaveProperty('payload_mass_lbs');
        expect(payload).toHaveProperty('orbit');
        expect(payload).toHaveProperty('orbit_params');
        expect(payload).toHaveProperty('orbit_params.reference_system');
        expect(payload).toHaveProperty('orbit_params.regime');
        expect(payload).toHaveProperty('orbit_params.longitude');
        expect(payload).toHaveProperty('orbit_params.semi_major_axis_km');
        expect(payload).toHaveProperty('orbit_params.eccentricity');
        expect(payload).toHaveProperty('orbit_params.periapsis_km');
        expect(payload).toHaveProperty('orbit_params.apoapsis_km');
        expect(payload).toHaveProperty('orbit_params.inclination_deg');
        expect(payload).toHaveProperty('orbit_params.period_min');
        expect(payload).toHaveProperty('orbit_params.lifespan_years');
        expect(payload).toHaveProperty('mass_returned_kg');
        expect(payload).toHaveProperty('mass_returned_lbs');
        expect(payload).toHaveProperty('flight_time_sec');
        expect(payload).toHaveProperty('cargo_manifest');
      });
    } else {
      item.rocket.second_stage.payloads.forEach((payload) => {
        expect(payload).toHaveProperty('payload_id');
        expect(payload).toHaveProperty('reused');
        expect(payload.customers.length).toBeGreaterThan(0);
        expect(payload).toHaveProperty('payload_mass_kg');
        expect(payload).toHaveProperty('payload_mass_lbs');
        expect(payload).toHaveProperty('orbit');
        expect(payload).toHaveProperty('orbit_params');
        expect(payload).toHaveProperty('orbit_params.reference_system');
        expect(payload).toHaveProperty('orbit_params.regime');
        expect(payload).toHaveProperty('orbit_params.longitude');
        expect(payload).toHaveProperty('orbit_params.semi_major_axis_km');
        expect(payload).toHaveProperty('orbit_params.eccentricity');
        expect(payload).toHaveProperty('orbit_params.periapsis_km');
        expect(payload).toHaveProperty('orbit_params.apoapsis_km');
        expect(payload).toHaveProperty('orbit_params.inclination_deg');
        expect(payload).toHaveProperty('orbit_params.period_min');
        expect(payload).toHaveProperty('orbit_params.lifespan_years');
      });
    }
    expect(item).toHaveProperty('telemetry.flight_club');
    expect(item).toHaveProperty('reuse.core');
    expect(item).toHaveProperty('reuse.side_core1');
    expect(item).toHaveProperty('reuse.side_core2');
    expect(item).toHaveProperty('reuse.fairings');
    expect(item).toHaveProperty('reuse.capsule');
    expect(item).toHaveProperty('launch_site.site_id');
    expect(item).toHaveProperty('launch_site.site_name');
    expect(item).toHaveProperty('launch_site.site_name_long');
    expect(item).toHaveProperty('launch_success');
    expect(item).toHaveProperty('links');
    expect(item).toHaveProperty('details');
  });
});

//------------------------------------------------------------
//                       Latest Launch
//------------------------------------------------------------

test('It should return the latest launch', async () => {
  const response = await request(app.callback()).get('/v2/launches/latest');
  expect(response.statusCode).toBe(200);
  expect(response.body).toHaveProperty('upcoming', false);
});

//------------------------------------------------------------
//                       Next Launch
//------------------------------------------------------------

test('It should return the next launch', async () => {
  const response = await request(app.callback()).get('/v2/launches/next');
  expect(response.statusCode).toBe(200);
  expect(response.body).toHaveProperty('upcoming', true);
});

//------------------------------------------------------------
//                       Past Launches
//------------------------------------------------------------

test('It should return the all launches', async () => {
  const response = await request(app.callback()).get('/v2/launches');
  expect(response.statusCode).toBe(200);
  response.body.forEach((launch) => {
    expect(launch.upcoming).toBe(false);
  });
});

test('It should return no launches due to invalid date', async () => {
  const response = await request(app.callback()).get('/v2/launches?start=2020-25-23&end=2020-25-24');
  expect(response.statusCode).toBe(200);
  expect(response.body).toEqual([]);
});

test('It should return no launches due to invalid UTC date', async () => {
  const response = await request(app.callback()).get('/v2/launches?launch_date_utc=2011-25-05T14:48:00.000Z');
  expect(response.statusCode).toBe(200);
  expect(response.body).toEqual([]);
});

//------------------------------------------------------------
//                       Upcoming Launches
//------------------------------------------------------------

test('It should return the next launch', async () => {
  const response = await request(app.callback()).get('/v2/launches/upcoming');
  expect(response.statusCode).toBe(200);
  response.body.forEach((launch) => {
    expect(launch.upcoming).toBe(true);
  });
});
<|endoftext|>


<commit_before>/*global require, exports*/
var util = require('util'),
    _ = require('underscore'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    HtmlRelation = require('./HtmlRelation');

function HtmlVideo(config) {
    HtmlRelation.call(this, config);
}

util.inherits(HtmlVideo, HtmlRelation);

extendWithGettersAndSetters(HtmlVideo.prototype, {
    get href() {
        return this.node.getAttribute('src');
    },

    set href(href) {
        this.node.setAttribute('src', href);
    },

    attach: function (asset, position, adjacentRelation) {
        this.node = asset.parseTree.createElement('video');
        this.attachNodeBeforeOrAfter(position, adjacentRelation);
        return HtmlRelation.prototype.attach.call(this, asset, position, adjacentRelation);
    }
});

module.exports = HtmlVideo;
<commit_msg>Removed unused _ in HtmlVideo<commit_after>var util = require('util'),
    extendWithGettersAndSetters = require('../util/extendWithGettersAndSetters'),
    HtmlRelation = require('./HtmlRelation');

function HtmlVideo(config) {
    HtmlRelation.call(this, config);
}

util.inherits(HtmlVideo, HtmlRelation);

extendWithGettersAndSetters(HtmlVideo.prototype, {
    get href() {
        return this.node.getAttribute('src');
    },

    set href(href) {
        this.node.setAttribute('src', href);
    },

    attach: function (asset, position, adjacentRelation) {
        this.node = asset.parseTree.createElement('video');
        this.attachNodeBeforeOrAfter(position, adjacentRelation);
        return HtmlRelation.prototype.attach.call(this, asset, position, adjacentRelation);
    }
});

module.exports = HtmlVideo;
<|endoftext|>


<commit_before>var express = require('express'),
    router  = express.Router(),
    Promise = require('bluebird'),
    debug   = require('debug')('sabesp:routes'),
    moment  = require('moment'),
    Mongo   = require('../lib/Mongo'),
    Helper  = require('../lib/Helper'),
    Sabesp  = require('../lib/Sabesp'),
    api     = require('../lib/APIVersions'),
    token;

Sabesp.getToken()
  .then(function(resolve) {
    token = resolve;
  });

router.use(function(request, response, next) {
  var date = request.url.replace('/', '');
  if (!date || Helper._isValidDateFormat(date)) {
    return next();
  }

  var err = new Error('Invalid date format');
  err.statusCode = 400;
  next(err);
});

router.get('/v1/:date?', function (req, res, next) {
  var date = req.params.date || Helper.today();
  _isCached(date)
    .then(function(resolve) {
      api.v1(resolve, res);
    })
    .catch(next);
});

router.get('/v2/:date?', function (req, res, next) {
  var date = req.params.date || Helper.today();
  _isCached(date)
    .then(function(resolve) {
      api.v2(resolve, res);
    })
    .catch(next);
});

router.get('/:date?', function (req, res, next) {
  var isNotValidDate = req.params.date !== undefined && !Helper._isValidDateFormat(req.params.date);
  if (isNotValidDate) {
    return next();
  }
  var date = req.params.date || Helper.today();
  _isCached(date)
    .then(function(resolve) {
      api.v0(resolve, res);
    })
    .catch(next);
});

router.get('/loaderio-deb75e3581d893735fd6e5050757bdb2', function (req, res, next) {
  res.send('loaderio-deb75e3581d893735fd6e5050757bdb2');
});

function _isCached (date) {
  return new Promise(function(resolve, reject) {
    Mongo.findOne('dams', { date: date }, function(err, result) {
      if (result) {
        resolve(result);
      } else {
        Sabesp.fetch(date, token).then(function(result) {
          if (_isAcceptableDate(result, date)) {
            Mongo.insert('dams', result, function(err, result) {
              if (err) { debug('err', err); }
            });
          }
          resolve(result);
        })
        .catch(function(err) {
          reject(err);
        });
      }
    });
  });
}

function _isAcceptableDate(result, date) {
  return (date !== '') && (moment(date) < moment(Helper.today())) && (result.date !== '');
}

module.exports = router;
<commit_msg>Fix validation<commit_after>var express = require('express'),
    router  = express.Router(),
    Promise = require('bluebird'),
    debug   = require('debug')('sabesp:routes'),
    moment  = require('moment'),
    Mongo   = require('../lib/Mongo'),
    Helper  = require('../lib/Helper'),
    Sabesp  = require('../lib/Sabesp'),
    api     = require('../lib/APIVersions'),
    token;

Sabesp.getToken()
  .then(function(resolve) {
    token = resolve;
  });

router.use(function(request, response, next) {
  var date = request.url.replace(/(.*)(\d{4}-\d{2}-\d{2})/, '$2');

  if (!date || Helper._isValidDateFormat(date) || /(\/v2|\/v1)?\//.test(date)) {
    return next();
  }

  var err = new Error('Invalid date format');
  err.statusCode = 400;
  next(err);
});

router.get('/v1/:date?', function (req, res, next) {
  var date = req.params.date || Helper.today();
  _isCached(date)
    .then(function(resolve) {
      api.v1(resolve, res);
    })
    .catch(next);
});

router.get('/v2/:date?', function (req, res, next) {
  var date = req.params.date || Helper.today();
  _isCached(date)
    .then(function(resolve) {
      api.v2(resolve, res);
    })
    .catch(next);
});

router.get('/:date?', function (req, res, next) {
  var isNotValidDate = req.params.date !== undefined && !Helper._isValidDateFormat(req.params.date);
  if (isNotValidDate) {
    return next();
  }
  var date = req.params.date || Helper.today();
  _isCached(date)
    .then(function(resolve) {
      api.v0(resolve, res);
    })
    .catch(next);
});

router.get('/loaderio-deb75e3581d893735fd6e5050757bdb2', function (req, res, next) {
  res.send('loaderio-deb75e3581d893735fd6e5050757bdb2');
});

function _isCached (date) {
  return new Promise(function(resolve, reject) {
    Mongo.findOne('dams', { date: date }, function(err, result) {
      if (result) {
        resolve(result);
      } else {
        Sabesp.fetch(date, token).then(function(result) {
          if (_isAcceptableDate(result, date)) {
            Mongo.insert('dams', result, function(err, result) {
              if (err) { debug('err', err); }
            });
          }
          resolve(result);
        })
        .catch(function(err) {
          reject(err);
        });
      }
    });
  });
}

function _isAcceptableDate(result, date) {
  return (date !== '') && (moment(date) < moment(Helper.today())) && (result.date !== '');
}

module.exports = router;
<|endoftext|>


<commit_before>const REQUIRED_ENV_VARIABLES = [
  'TYMLY_GITHUB_TOKEN',
  'TYMLY_GITHUB_USER'
]
const path = require('path')

let LernaSync
try {
  // If the local lerna-sync package is available, then use that.
  LernaSync = require('../packages/lerna-sync')
} catch (e) {
  // If it's not been synced in yet and not available, then fallback to the lerna-sync dependency.
  // (as expected via an npm install).
  LernaSync = require('@wmfs/lerna-sync')
}

const process = require('process')

async function main () {
  REQUIRED_ENV_VARIABLES.forEach(
    varName => {
      if (!process.env.hasOwnProperty(varName)) {
        throw new Error(`Required environment variable ${varName} has not set!`)
      }
    }
  )

  const lernaSync = new LernaSync(
    {
      monorepoPath: path.resolve(__dirname, '..'),
      gitHubToken: process.env.TYMLY_GITHUB_TOKEN,
      gitHubOrgName: 'wmfs',
      gitHubUser: process.env.TYMLY_GITHUB_USER,
      lernaPackageRouterFunction: packageObjCallback
    }
  )
  await lernaSync.sync()
}

(async () => {
  await main()
})().catch(e => {
  console.error(e)
})

function packageObjCallback (gitHubPackageObj) {
  if (!gitHubPackageObj) return

  if (!isTymlyPackage(gitHubPackageObj)) return

  const keywordToPackageMap = [
    ['package', 'packages'],
    ['plugin', 'plugins'],
    ['blueprint', 'blueprints'],
    ['cardscript', 'cardscript'],
    ['app', 'apps'],
    ['mod', 'mods']
  ]

  let lernaPackageName = null
  const keywords = gitHubPackageObj.keywords

  keywordToPackageMap.forEach(
    ([keyword, packageName]) => {
      if (keywords.indexOf(keyword) !== -1) {
        lernaPackageName = packageName
      }
    }
  )
  return lernaPackageName
}

function isTymlyPackage(gitHubPackageObj) {
  return (gitHubPackageObj.keywords) && (gitHubPackageObj.keywords.indexOf('tymly') !== -1)
}
<commit_msg>feat: Skip packages with config.tymly.sync == false in their package.json<commit_after>const REQUIRED_ENV_VARIABLES = [
  'TYMLY_GITHUB_TOKEN',
  'TYMLY_GITHUB_USER'
]
const path = require('path')

let LernaSync
try {
  // If the local lerna-sync package is available, then use that.
  LernaSync = require('../packages/lerna-sync')
} catch (e) {
  // If it's not been synced in yet and not available, then fallback to the lerna-sync dependency.
  // (as expected via an npm install).
  LernaSync = require('@wmfs/lerna-sync')
}

const process = require('process')

async function main () {
  REQUIRED_ENV_VARIABLES.forEach(
    varName => {
      if (!process.env.hasOwnProperty(varName)) {
        throw new Error(`Required environment variable ${varName} has not set!`)
      }
    }
  )

  const lernaSync = new LernaSync(
    {
      monorepoPath: path.resolve(__dirname, '..'),
      gitHubToken: process.env.TYMLY_GITHUB_TOKEN,
      gitHubOrgName: 'wmfs',
      gitHubUser: process.env.TYMLY_GITHUB_USER,
      lernaPackageRouterFunction: packageObjCallback
    }
  )
  await lernaSync.sync()
}

(async () => {
  await main()
})().catch(e => {
  console.error(e)
})

function packageObjCallback (gitHubPackageObj) {
  if (!gitHubPackageObj) return

  if (!isTymlyPackage(gitHubPackageObj)) return

  if (!shouldSync(gitHubPackageObj)) return

  const keywordToPackageMap = [
    ['package', 'packages'],
    ['plugin', 'plugins'],
    ['blueprint', 'blueprints'],
    ['cardscript', 'cardscript'],
    ['app', 'apps'],
    ['mod', 'mods']
  ]

  let lernaPackageName = null
  const keywords = gitHubPackageObj.keywords

  keywordToPackageMap.forEach(
    ([keyword, packageName]) => {
      if (keywords.indexOf(keyword) !== -1) {
        lernaPackageName = packageName
      }
    }
  )
  return lernaPackageName
}

function isTymlyPackage (gitHubPackageObj) {
  return (gitHubPackageObj.keywords) && (gitHubPackageObj.keywords.indexOf('tymly') !== -1)
}

function shouldSync (gitHubPackageObj) {
  if (gitHubPackageObj.config && gitHubPackageObj.config.tymly)
    return gitHubPackageObj.config.tymly.sync !== false
  return true
}
<|endoftext|>


<commit_before>var MongoClient = require('mongodb').MongoClient;

/**
 * search all talks
 * @param indexpath full-text index path string
 * @param callback  function to execute with the resulting data
 */
exports.search = function(index, q, callback) {
    "use strict";
    var si = require('search-index')({
        indexPath: index
    });
    si.search(q, function(err, results) {
        if (err) {
            throw new Error(err);
        }
        return callback(null, results);
    });
};

/**
 * get all the talks
 * @param dburl     database url string
 * @param callback  function to execute with the resulting data
 */
exports.all = function(dburl, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.
        find({}).
        toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get the latest talks
 * @param dburl     database url string
 * @param quantity  number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.latest = function(dburl, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.
        find({}).
        sort({
            created: -1
        }).
        limit(quantity).
        toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get the most popular talks
 * @param dburl     database url string
 * @param quantity  number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.popular = function(dburl, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.
        find({}).
        sort({
            viewCount: -1
        }).
        limit(quantity).
        toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * create a new talk
 * @param dburl     database url string
 * @param obj       number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.createTalk = function(dburl, obj, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.insert(obj, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * delete a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.deleteTalk = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.remove({
            id: id
        }, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.get = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            id: id
        }, function(err, doc) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, doc);
        });
    });
};

/**
 * upvote a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that upvotes the talk
 * @param callback  function to execute with the resulting data
 */
exports.upvote = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "votes": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "voteCount": 1
            },
            "$push": {
                "votes": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            return callback(null, talk);
        });
    });
};

/**
 * favorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.favorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "favoriteCount": 1
            },
            "$push": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            return callback(null, talk);
        });
    });
};

/**
 * unfavorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.unfavorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$in": [userid]
            }
        }, {
            "$inc": {
                "favoriteCount": -1
            },
            "$pull": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            return callback(null, talk);
        });
    });
};

/**
 * play a talk
 * Updates views field by 1.
 * @param dburl     database url string
 * @param slug      Talk slug
 * @param callback  function to execute with the resulting data
 */
exports.play = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            "slug": slug
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            talk.viewCount = talk.viewCount + 1;
            talks.update({
                "slug": slug
            }, {
                $inc: {
                    "viewCount": 1
                },
                $set: {
                    updated: Date.now()
                }
            }, function(err) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, talk);
            });
        });
    });
};

/**
 * get a talk by tag
 * @param dburl     database url string
 * @param tag       talk tag string
 * @param callback  function to execute with the resulting data
 */
exports.getByTag = function(dburl, tag, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            tags: tag
        }).sort({
            viewCount: -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
    return true;
};

/**
 * get a talk by its slug field
 * @param dburl     database url string
 * @param slug      talk slug string
 * @param callback  function to execute with the resulting data
 */
exports.getBySlug = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "slug": slug
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get reltated talks from a talk
 * @param dburl     database url string
 * @param talk      talk object
 * @param callback  function to execute with the resulting data
 */
exports.related = function(dburl, talk, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "tags": {
                $in: talk.tags
            }
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        author id string
 * @param callback  function to execute with the resulting data
 */
exports.getByAuthorId = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "author.id": id
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks upvoted by an user
 * @param dburl     database url string
 * @param userid        author id string
 * @param callback  function to execute with the resulting data
 */
exports.getUpvotedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
             "votes": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks favorited by an user
 * @param dburl     database url string
 * @param userid    author id string
 * @param callback  function to execute with the resulting data
 */
exports.getFavoritedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
             "favorites": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};
<commit_msg>Added quantity argument for related talks<commit_after>var MongoClient = require('mongodb').MongoClient;

/**
 * search all talks
 * @param indexpath full-text index path string
 * @param callback  function to execute with the resulting data
 */
exports.search = function(index, q, callback) {
    "use strict";
    var si = require('search-index')({
        indexPath: index
    });
    si.search(q, function(err, results) {
        if (err) {
            throw new Error(err);
        }
        return callback(null, results);
    });
};

/**
 * get all the talks
 * @param dburl     database url string
 * @param callback  function to execute with the resulting data
 */
exports.all = function(dburl, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.
        find({}).
        toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get the latest talks
 * @param dburl     database url string
 * @param quantity  number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.latest = function(dburl, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.
        find({}).
        sort({
            created: -1
        }).
        limit(quantity).
        toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get the most popular talks
 * @param dburl     database url string
 * @param quantity  number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.popular = function(dburl, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.
        find({}).
        sort({
            viewCount: -1
        }).
        limit(quantity).
        toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * create a new talk
 * @param dburl     database url string
 * @param obj       number of results to get
 * @param callback  function to execute with the resulting data
 */
exports.createTalk = function(dburl, obj, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.insert(obj, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * delete a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.deleteTalk = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.remove({
            id: id
        }, function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        talk id string
 * @param callback  function to execute with the resulting data
 */
exports.get = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            id: id
        }, function(err, doc) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, doc);
        });
    });
};

/**
 * upvote a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that upvotes the talk
 * @param callback  function to execute with the resulting data
 */
exports.upvote = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "votes": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "voteCount": 1
            },
            "$push": {
                "votes": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            return callback(null, talk);
        });
    });
};

/**
 * favorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.favorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$ne": userid
            }
        }, {
            "$inc": {
                "favoriteCount": 1
            },
            "$push": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            return callback(null, talk);
        });
    });
};

/**
 * unfavorite a talk
 * @param dburl     database url string
 * @param id        Talk id
 * @param userid    User that favorites the talk
 * @param callback  function to execute with the resulting data
 */
exports.unfavorite = function(dburl, id, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.update({
            "id": id,
            "favorites": {
                "$in": [userid]
            }
        }, {
            "$inc": {
                "favoriteCount": -1
            },
            "$pull": {
                "favorites": userid
            }
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            return callback(null, talk);
        });
    });
};

/**
 * play a talk
 * Updates views field by 1.
 * @param dburl     database url string
 * @param slug      Talk slug
 * @param callback  function to execute with the resulting data
 */
exports.play = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.findOne({
            "slug": slug
        }, function(err, talk) {
            if (err) {
                return callback(err, null);
            }
            talk.viewCount = talk.viewCount + 1;
            talks.update({
                "slug": slug
            }, {
                $inc: {
                    "viewCount": 1
                },
                $set: {
                    updated: Date.now()
                }
            }, function(err) {
                if (err) {
                    return callback(err, null);
                }
                db.close();
                return callback(null, talk);
            });
        });
    });
};

/**
 * get a talk by tag
 * @param dburl     database url string
 * @param tag       talk tag string
 * @param callback  function to execute with the resulting data
 */
exports.getByTag = function(dburl, tag, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            tags: tag
        }).sort({
            viewCount: -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
    return true;
};

/**
 * get a talk by its slug field
 * @param dburl     database url string
 * @param slug      talk slug string
 * @param callback  function to execute with the resulting data
 */
exports.getBySlug = function(dburl, slug, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "slug": slug
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get reltated talks from a talk
 * @param dburl     database url string
 * @param talk      talk object
 * @param callback  function to execute with the resulting data
 */
exports.related = function(dburl, talk, quantity, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "tags": {
                $in: talk.tags
            }
        })
        limit(quantity).
        .toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get a talk by its id
 * @param dburl     database url string
 * @param id        author id string
 * @param callback  function to execute with the resulting data
 */
exports.getByAuthorId = function(dburl, id, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
            "author.id": id
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks upvoted by an user
 * @param dburl     database url string
 * @param userid        author id string
 * @param callback  function to execute with the resulting data
 */
exports.getUpvotedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
             "votes": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};

/**
 * get list of talks favorited by an user
 * @param dburl     database url string
 * @param userid    author id string
 * @param callback  function to execute with the resulting data
 */
exports.getFavoritedByAuthorId = function(dburl, userid, callback) {
    "use strict";
    MongoClient.connect(dburl, function(err, db) {
        if (err) {
            return callback(err, null);
        }
        var talks = db.collection('talks');
        talks.find({
             "favorites": {
                "$in": [userid]
            }
        }).sort({
            "created": -1
        }).toArray(function(err, docs) {
            if (err) {
                return callback(err, null);
            }
            db.close();
            return callback(null, docs);
        });
    });
};
<|endoftext|>


<commit_before>var Promise = require('bluebird');

var seq = exports.seq = function(promises){
    var results = [];
    var pushResult = Array.prototype.push.bind(results);
    return promises.reduce(function (prevPromise, promisedIdentity){
        return prevPromise.then(function (){
            return Promise.resolve(promisedIdentity()).then(pushResult);
        });
    }, Promise.resolve()).then(function (){
        return results;
    });
}

var all = exports.all = function(promises){
    return Promise.all(promises.map(function(v){ return v() }));
}

var limit = exports.limit = function(promises, max){
    var w = [];
    for(i=0; i<max; ++i) w.push([]);
    promises.forEach(function(promise, n){
        w[n % max].push(promise);
    })
    var seqs = w.map(function(v){
        return function(){
            return seq(v)
        }
    })
    return all(seqs).then(function(res){
        var w = new Array(res.reduce(function(r,v){return r+v.length},0));
        var len = res.length;
        for(i = 0; i<len; ++i){
            for(j=0; j<res[i].length; ++j){
                w[i + (j * len)] = res[i][j];
            }
        }
        return w;
    })
}

<commit_msg>add strict mode<commit_after>'use strict'
var Promise = require('bluebird');

var seq = exports.seq = function(promises){
    var results = [];
    var pushResult = Array.prototype.push.bind(results);
    return promises.reduce(function (prevPromise, promisedIdentity){
        return prevPromise.then(function (){
            return Promise.resolve(promisedIdentity()).then(pushResult);
        });
    }, Promise.resolve()).then(function (){
        return results;
    });
}

var all = exports.all = function(promises){
    return Promise.all(promises.map(function(v){ return v() }));
}

var limit = exports.limit = function(promises, max){
    var w = [];
    for(var i = 0; i < max; ++i) w.push([]);
    promises.forEach(function(promise, n){
        w[n % max].push(promise);
    })
    var seqs = w.map(function(v){
        return function(){
            return seq(v)
        }
    })
    return all(seqs).then(function(res){
        var w = new Array(res.reduce(function(r,v){return r+v.length},0));
        var len = res.length;
        for(var i = 0; i < len; ++i){
            for(var j = 0; j < res[i].length; ++j){
                w[i + (j * len)] = res[i][j];
            }
        }
        return w;
    })
}

<|endoftext|>


<commit_before>'use strict';

const bodyParser = require('koa-bodyparser');
const Joi = require('joi');
const validateNpmPackageName = require('validate-npm-package-name');
const keyBy = require('lodash/keyBy');
const uniq = require('lodash/uniq');
const joiHelper = require('../../util/joiHelper');

function validateNames(names) {
    names.forEach((name) => {
        const validation = validateNpmPackageName(name);

        if (validation.errors) {
            throw Object.assign(new Error(`${validation.errors[0]} ("${name}")`),
                { code: 'INVALID_PARAMETER', status: 400, expose: true });
        }
    });

    return names;
}

function fetchFromCouchdb(names, npmsNano) {
    return npmsNano.fetchAsync({ keys: names.map((name) => `package!${name}`) })
    .get('rows')
    .map((row) => {
        if (row.doc) {
            return {
                analyzedAt: row.doc.finishedAt,
                collected: row.doc.collected,
                evaluation: row.doc.evaluation,
                error: row.doc.error,
            };
        }

        if (row.error === 'not_found') {
            return null;
        }

        throw Object.assign(new Error(`Unable to retrieve ${row.key} from CouchDB`), { code: row.error });
    });
}

function fetchFromElasticsearch(names, esClient) {
    return Promise.resolve(esClient.mget({
        index: 'npms-current',
        type: 'score',
        body: { ids: names },
        _source: ['score'],
    }))
    .get('docs')
    .map((doc) => doc._source);
}

// ----------------------------------------------------------

module.exports = (router, npmsNano, esClient) => {
    /**
     * @api {get} /package/:name Get package info
     * @apiName GetPackageInfo
     * @apiGroup Package
     * @apiVersion 2.0.0
     *
     * @apiParam {string} name The package name
     *
     * @apiSuccess {string} analyzedAt  The date in which the package was last analyzed
     * @apiSuccess {object} collected   The collected information from all sources
     * @apiSuccess {object} evaluation  The package evaluation
     * @apiSuccess {object} score       The package score
     *
     * @apiSuccessExample {json} Response
     *     {
     *       "analyzedAt": "2016-07-04T23:04:54.646Z",
     *       "collected": { "metadata": { ... }, "npm": { ... }, ... },
     *       "evaluation": { "quality": { ... }, "popularity": { ... }, ... }
     *       "score": { "final": 0.89, "detail": { "quality": 0.99, "popularity": 0.77, ... } },
     *       "error": { ... }
     *     }
     *
     * @apiExample {curl} Usage
     *     curl "https://api.npms.io/v2/package/react"
     */
    const infoParamsSchema = Joi.object({
        name: Joi.string().trim().min(1).required(),
    }).required();

    router.get('/package/:name',
        function * (next) {
            this.validated = joiHelper.validate(infoParamsSchema, this.params);
            validateNames([this.validated.name]);
            yield next;
        },
        function * () {
            const names = [this.validated.name];

            this.log.debug({ names }, 'Will fetch info');

            // Fetch info
            const info = yield Promise.props({
                couchdb: fetchFromCouchdb(names, npmsNano).get(0),
                elasticsearch: fetchFromElasticsearch(names, esClient).get(0),
            });

            this.log.debug({ names, info }, 'Got info');

            // Check if the package does not exist
            if (!info.couchdb || !info.elasticsearch) {
                throw Object.assign(new Error('Module not found'), { code: 'NOT_FOUND', status: 404, expose: true });
            }

            this.body = Object.assign({}, info.couchdb, info.elasticsearch);
        });

    /**
     * @api {post} /package/mget Get various packages info
     * @apiName GetMultiPackageInfo
     * @apiGroup Package
     * @apiVersion 2.0.0
     *
     * @apiParam {array} names The package names, specified as the request body
     *
     * @apiSuccess (Each info has) {string} analyzedAt  The date in which the package was last analyzed
     * @apiSuccess (Each info has) {object} collected   The collected information from all sources
     * @apiSuccess (Each info has) {object} evaluation  The package evaluation
     * @apiSuccess (Each info has) {object} score       The package score
     *
     * @apiSuccessExample {json} Response
     *     {
     *       "cross-spawn": {
     *         "analyzedAt": "2016-07-04T23:04:54.646Z",
     *         "collected": { "metadata": { ... }, "npm": { ... }, ... },
     *         "evaluation": { "quality": { ... }, "popularity": { ... }, ... }
     *         "score": { "final": 0.89, "detail": { "quality": 0.99, "popularity": 0.77, ... } }
     *         "error": { ... }
     *       },
     *       "react": {
     *         "analyzedAt": "2016-07-03T11:23:04.553Z",
     *         "collected": { "metadata": { ... }, "npm": { ... }, ... },
     *         "evaluation": { "quality": { ... }, "popularity": { ... }, ... }
     *         "score": { "final": 0.95, "detail": { "quality": 0.99, "popularity": 0.93, ... } }
     *         "error": { ... }
     *       }
     *     }
     *
     * @apiExample {curl} Example usage
     *     curl -X POST "https://api.npms.io/v2/package/mget" \
     *     	    -H "Accept: application/json" \
     *     	    -H "Content-Type: application/json" \
     *     	    -d '["cross-spawn", "react"]'
     */
    const infoMgetBodySchema = Joi.array().label('names').min(1).max(250).items(Joi.string().trim().min(1)).required();

    router.post('/package/mget',
        bodyParser({ enableTypes: ['json'] }),
        function * (next) {
            this.validated = validateNames(joiHelper.validate(infoMgetBodySchema, uniq(this.request.body)));
            yield next;
        },
        function * () {
            const names = this.validated;

            this.log.debug({ names }, 'Will fetch info');

            // Fetch info
            const info = yield Promise.props({
                couchdb: fetchFromCouchdb(names, npmsNano),
                elasticsearch: fetchFromElasticsearch(names, esClient),
            });

            this.log.debug({ names, info }, 'Got info');

            // Build the packages array, clearing the ones that do not exist
            const infos = info.couchdb
            .map((infoCouchdb, index) => {
                const infoElasticsearch = info.elasticsearch[index];

                return !infoCouchdb || !infoElasticsearch ? null :
                    Object.assign({}, infoCouchdb, infoElasticsearch);
            })
            .filter((info) => !!info);

            // Return an object indexed by name
            this.body = keyBy(infos, (info) => info.collected.metadata.name);
        });
};
<commit_msg>Update api doc.<commit_after>'use strict';

const bodyParser = require('koa-bodyparser');
const Joi = require('joi');
const validateNpmPackageName = require('validate-npm-package-name');
const keyBy = require('lodash/keyBy');
const uniq = require('lodash/uniq');
const joiHelper = require('../../util/joiHelper');

function validateNames(names) {
    names.forEach((name) => {
        const validation = validateNpmPackageName(name);

        if (validation.errors) {
            throw Object.assign(new Error(`${validation.errors[0]} ("${name}")`),
                { code: 'INVALID_PARAMETER', status: 400, expose: true });
        }
    });

    return names;
}

function fetchFromCouchdb(names, npmsNano) {
    return npmsNano.fetchAsync({ keys: names.map((name) => `package!${name}`) })
    .get('rows')
    .map((row) => {
        if (row.doc) {
            return {
                analyzedAt: row.doc.finishedAt,
                collected: row.doc.collected,
                evaluation: row.doc.evaluation,
                error: row.doc.error,
            };
        }

        if (row.error === 'not_found') {
            return null;
        }

        throw Object.assign(new Error(`Unable to retrieve ${row.key} from CouchDB`), { code: row.error });
    });
}

function fetchFromElasticsearch(names, esClient) {
    return Promise.resolve(esClient.mget({
        index: 'npms-current',
        type: 'score',
        body: { ids: names },
        _source: ['score'],
    }))
    .get('docs')
    .map((doc) => doc._source);
}

// ----------------------------------------------------------

module.exports = (router, npmsNano, esClient) => {
    /**
     * @api {get} /package/:name Get package info
     * @apiName GetPackageInfo
     * @apiGroup Package
     * @apiVersion 2.0.0
     *
     * @apiParam {string} name The package name
     *
     * @apiSuccess {string} analyzedAt  The date in which the package was last analyzed
     * @apiSuccess {object} collected   The collected information from all sources
     * @apiSuccess {object} evaluation  The package evaluation
     * @apiSuccess {object} score       The package score
     * @apiSuccess {object} [error]     The error associated with the last analyze attempt
     *
     * @apiSuccessExample {json} Response
     *     {
     *       "analyzedAt": "2016-07-04T23:04:54.646Z",
     *       "collected": { "metadata": { ... }, "npm": { ... }, ... },
     *       "evaluation": { "quality": { ... }, "popularity": { ... }, ... }
     *       "score": { "final": 0.89, "detail": { "quality": 0.99, "popularity": 0.77, ... } },
     *       "error": { ... }
     *     }
     *
     * @apiExample {curl} Usage
     *     curl "https://api.npms.io/v2/package/react"
     */
    const infoParamsSchema = Joi.object({
        name: Joi.string().trim().min(1).required(),
    }).required();

    router.get('/package/:name',
        function * (next) {
            this.validated = joiHelper.validate(infoParamsSchema, this.params);
            validateNames([this.validated.name]);
            yield next;
        },
        function * () {
            const names = [this.validated.name];

            this.log.debug({ names }, 'Will fetch info');

            // Fetch info
            const info = yield Promise.props({
                couchdb: fetchFromCouchdb(names, npmsNano).get(0),
                elasticsearch: fetchFromElasticsearch(names, esClient).get(0),
            });

            this.log.debug({ names, info }, 'Got info');

            // Check if the package does not exist
            if (!info.couchdb || !info.elasticsearch) {
                throw Object.assign(new Error('Module not found'), { code: 'NOT_FOUND', status: 404, expose: true });
            }

            this.body = Object.assign({}, info.couchdb, info.elasticsearch);
        });

    /**
     * @api {post} /package/mget Get various packages info
     * @apiName GetMultiPackageInfo
     * @apiGroup Package
     * @apiVersion 2.0.0
     *
     * @apiParam {array} names The package names, specified as the request body
     *
     * @apiSuccess (Each info has) {string} analyzedAt  The date in which the package was last analyzed
     * @apiSuccess (Each info has) {object} collected   The collected information from all sources
     * @apiSuccess (Each info has) {object} evaluation  The package evaluation
     * @apiSuccess (Each info has) {object} score       The package score
     * @apiSuccess (Each info has) {object} [error]     The error associated with the last analyze attempt
     *
     * @apiSuccessExample {json} Response
     *     {
     *       "cross-spawn": {
     *         "analyzedAt": "2016-07-04T23:04:54.646Z",
     *         "collected": { "metadata": { ... }, "npm": { ... }, ... },
     *         "evaluation": { "quality": { ... }, "popularity": { ... }, ... }
     *         "score": { "final": 0.89, "detail": { "quality": 0.99, "popularity": 0.77, ... } }
     *         "error": { ... }
     *       },
     *       "react": {
     *         "analyzedAt": "2016-07-03T11:23:04.553Z",
     *         "collected": { "metadata": { ... }, "npm": { ... }, ... },
     *         "evaluation": { "quality": { ... }, "popularity": { ... }, ... }
     *         "score": { "final": 0.95, "detail": { "quality": 0.99, "popularity": 0.93, ... } }
     *         "error": { ... }
     *       }
     *     }
     *
     * @apiExample {curl} Example usage
     *     curl -X POST "https://api.npms.io/v2/package/mget" \
     *     	    -H "Accept: application/json" \
     *     	    -H "Content-Type: application/json" \
     *     	    -d '["cross-spawn", "react"]'
     */
    const infoMgetBodySchema = Joi.array().label('names').min(1).max(250).items(Joi.string().trim().min(1)).required();

    router.post('/package/mget',
        bodyParser({ enableTypes: ['json'] }),
        function * (next) {
            this.validated = validateNames(joiHelper.validate(infoMgetBodySchema, uniq(this.request.body)));
            yield next;
        },
        function * () {
            const names = this.validated;

            this.log.debug({ names }, 'Will fetch info');

            // Fetch info
            const info = yield Promise.props({
                couchdb: fetchFromCouchdb(names, npmsNano),
                elasticsearch: fetchFromElasticsearch(names, esClient),
            });

            this.log.debug({ names, info }, 'Got info');

            // Build the packages array, clearing the ones that do not exist
            const infos = info.couchdb
            .map((infoCouchdb, index) => {
                const infoElasticsearch = info.elasticsearch[index];

                return !infoCouchdb || !infoElasticsearch ? null :
                    Object.assign({}, infoCouchdb, infoElasticsearch);
            })
            .filter((info) => !!info);

            // Return an object indexed by name
            this.body = keyBy(infos, (info) => info.collected.metadata.name);
        });
};
<|endoftext|>


<commit_before>var http = require('http'),
  net = require('net'),
  https = require('https'),
  fs = require('fs'),
  os = require('os'),
  request = require('request'),
  url = require('url'),
  EventEmitter = require('events').EventEmitter,
  util = require('util');

var Logger = require('./logger');

function Mitm(opts) {
  this.opts = opts || {};

  // socket path for system mitm https server
  this.socket = os.tmpdir() + '/node-thin.' + process.pid + '.sock';
  this.interceptors = [];

  this.log = new Logger(false);
}

util.inherits(Mitm, EventEmitter);

Mitm.prototype.listen = function(port, host, cb) {
  var self = this;

  // make sure there's no previously created socket
  if (fs.existsSync(this.socket))
    fs.unlinkSync(this.socket);

  var options = {
    key: fs.readFileSync(__dirname + '/../cert/dummy.key', 'utf8'),
    cert: fs.readFileSync(__dirname + '/../cert/dummy.crt', 'utf8')
  };
  // fake https server, MITM if you want
  this.httpsServer = https.createServer(options, this._handler.bind(this)).listen(this.socket);

  // start HTTP server with custom request handler callback function
  this.httpServer = http.createServer(this._handler.bind(this)).listen(port, host, function(err) {
    if (err)
      self.log.error('Cannot start proxy', err);
    // else
    //   this.log.info('Listening on %s:%s [%d]', this.opts.host, this.opts.port, process.pid);
    cb(err);
  });

  // add handler for HTTPS (which issues a CONNECT to the proxy)
  this.httpServer.addListener('connect', this._httpsHandler.bind(this));
};

Mitm.prototype.close = function(cb) {
  var self = this;
  self.httpServer.close(function(err) {
    if (err)
      return cb(err);
    self.httpsServer.close(cb);
  });
};

Mitm.prototype._handler = function(req, res) {

  var interceptors = this.interceptors.concat([this.direct.bind(this)]);
  var layer = 0;

  (function runner() {
    var interceptor = interceptors[layer++];
    interceptor(req, res, function(err) {
      if (err) return res.end('Proxy error: ' + err.toString());
      runner();
    });
  })();
};

Mitm.prototype._httpsHandler = function(request, socketRequest, bodyhead) {
  var self = this;
  var log = this.log,
    url = request['url'],
    httpVersion = request['httpVersion'];

  log.info('  = will connect to socket "%s"', this.socket);

  // set up TCP connection
  var proxySocket = new net.Socket();
  proxySocket.connect(this.socket, function() {
    log.info('< connected to socket "%s"', self.socket);
    log.info('> writing head of length %d', bodyhead.length);

    proxySocket.write(bodyhead);

    // tell the caller the connection was successfully established
    socketRequest.write('HTTP/' + httpVersion + ' 200 Connection established\r\n\r\n');
  });

  proxySocket.on('data', function(chunk) {
    log.info('< data length = %d', chunk.length);
    socketRequest.write(chunk);
  });

  proxySocket.on('end', function() {
    log.info('< end');
    socketRequest.end();
  });

  socketRequest.on('data', function(chunk) {
    log.info('> data length = %d', chunk.length);
    proxySocket.write(chunk);
  });

  socketRequest.on('end', function() {
    log.info('> end');
    proxySocket.end();
  });

  proxySocket.on('error', function(err) {
    socketRequest.write('HTTP/' + httpVersion + ' 500 Connection error\r\n\r\n');
    log.error('< ERR: %s', err);
    socketRequest.end();
  });

  socketRequest.on('error', function(err) {
    log.error('> ERR: %s', err);
    proxySocket.end();
  });
};

Mitm.prototype.use = function(fn) {
  this.interceptors.push(fn);
};

Mitm.prototype.removeInterceptors = function() {
  this.interceptors.length = 0;
};

Mitm.prototype.direct = function(req, res) {
  var log = this.log,
    self = this,
    path = url.parse(req.url).path,
    schema = Boolean(req.client.pair) ? 'https' : 'http',
    dest = schema + '://' + req.headers['host'] + path;

  var params = {
    url: dest,
    strictSSL: this.opts.strictSSL,
    rejectUnauthorized: this.opts.rejectUnauthorized,
    method: req.method,
    proxy: this.opts.proxy,
    headers: {}
  };

  // Set original headers except proxy system's headers
  var exclude = ['proxy-connection'];
  for (var hname in req.headers)
    if (!~exclude.indexOf(hname))
      params.headers[hname] = req.headers[hname];

  var buffer = '';
  req.on('data', function(chunk) {
    buffer += chunk;
  });

  req.on('end', function() {
    params.body = buffer;
    var r = request(params, function(err, response) {
      if (err) {
          self.emit('error', err);
          res.end();
      }
    });

    r.pipe(res);
  });

};

module.exports = Mitm;
<commit_msg>Disable following redirects, this breaks sites using OAuth2 to log in and makes the proxy less "transparent".<commit_after>var http = require('http'),
  net = require('net'),
  https = require('https'),
  fs = require('fs'),
  os = require('os'),
  request = require('request'),
  url = require('url'),
  EventEmitter = require('events').EventEmitter,
  util = require('util');

var Logger = require('./logger');

function Mitm(opts) {
  this.opts = opts || {};

  // socket path for system mitm https server
  this.socket = os.tmpdir() + '/node-thin.' + process.pid + '.sock';
  this.interceptors = [];

  this.log = new Logger(false);
}

util.inherits(Mitm, EventEmitter);

Mitm.prototype.listen = function(port, host, cb) {
  var self = this;

  // make sure there's no previously created socket
  if (fs.existsSync(this.socket))
    fs.unlinkSync(this.socket);

  var options = {
    key: fs.readFileSync(__dirname + '/../cert/dummy.key', 'utf8'),
    cert: fs.readFileSync(__dirname + '/../cert/dummy.crt', 'utf8')
  };
  // fake https server, MITM if you want
  this.httpsServer = https.createServer(options, this._handler.bind(this)).listen(this.socket);

  // start HTTP server with custom request handler callback function
  this.httpServer = http.createServer(this._handler.bind(this)).listen(port, host, function(err) {
    if (err)
      self.log.error('Cannot start proxy', err);
    // else
    //   this.log.info('Listening on %s:%s [%d]', this.opts.host, this.opts.port, process.pid);
    cb(err);
  });

  // add handler for HTTPS (which issues a CONNECT to the proxy)
  this.httpServer.addListener('connect', this._httpsHandler.bind(this));
};

Mitm.prototype.close = function(cb) {
  var self = this;
  self.httpServer.close(function(err) {
    if (err)
      return cb(err);
    self.httpsServer.close(cb);
  });
};

Mitm.prototype._handler = function(req, res) {

  var interceptors = this.interceptors.concat([this.direct.bind(this)]);
  var layer = 0;

  (function runner() {
    var interceptor = interceptors[layer++];
    interceptor(req, res, function(err) {
      if (err) return res.end('Proxy error: ' + err.toString());
      runner();
    });
  })();
};

Mitm.prototype._httpsHandler = function(request, socketRequest, bodyhead) {
  var self = this;
  var log = this.log,
    url = request['url'],
    httpVersion = request['httpVersion'];

  log.info('  = will connect to socket "%s"', this.socket);

  // set up TCP connection
  var proxySocket = new net.Socket();
  proxySocket.connect(this.socket, function() {
    log.info('< connected to socket "%s"', self.socket);
    log.info('> writing head of length %d', bodyhead.length);

    proxySocket.write(bodyhead);

    // tell the caller the connection was successfully established
    socketRequest.write('HTTP/' + httpVersion + ' 200 Connection established\r\n\r\n');
  });

  proxySocket.on('data', function(chunk) {
    log.info('< data length = %d', chunk.length);
    socketRequest.write(chunk);
  });

  proxySocket.on('end', function() {
    log.info('< end');
    socketRequest.end();
  });

  socketRequest.on('data', function(chunk) {
    log.info('> data length = %d', chunk.length);
    proxySocket.write(chunk);
  });

  socketRequest.on('end', function() {
    log.info('> end');
    proxySocket.end();
  });

  proxySocket.on('error', function(err) {
    socketRequest.write('HTTP/' + httpVersion + ' 500 Connection error\r\n\r\n');
    log.error('< ERR: %s', err);
    socketRequest.end();
  });

  socketRequest.on('error', function(err) {
    log.error('> ERR: %s', err);
    proxySocket.end();
  });
};

Mitm.prototype.use = function(fn) {
  this.interceptors.push(fn);
};

Mitm.prototype.removeInterceptors = function() {
  this.interceptors.length = 0;
};

Mitm.prototype.direct = function(req, res) {
  var log = this.log,
    self = this,
    path = url.parse(req.url).path,
    schema = Boolean(req.client.pair) ? 'https' : 'http',
    dest = schema + '://' + req.headers['host'] + path;

  var params = {
    url: dest,
    strictSSL: this.opts.strictSSL,
    rejectUnauthorized: this.opts.rejectUnauthorized,
    method: req.method,
    proxy: this.opts.proxy,
    followRedirect: false,
    headers: {}
  };

  // Set original headers except proxy system's headers
  var exclude = ['proxy-connection'];
  for (var hname in req.headers)
    if (!~exclude.indexOf(hname))
      params.headers[hname] = req.headers[hname];

  var buffer = '';
  req.on('data', function(chunk) {
    buffer += chunk;
  });

  req.on('end', function() {
    params.body = buffer;
    var r = request(params, function(err, response) {
      if (err) {
          self.emit('error', err);
          res.end();
      }
    });

    r.pipe(res);
  });

};

module.exports = Mitm;
<|endoftext|>


<commit_before>var maps = require('@google/maps')
var config = require('dotenv').config();
var googleMapsClient = maps.createClient({
  key: process.env.GOOGLE_API_KEY
});

var tripPoints = [];

/**
 * Generate a trip with params
 *
 * @param {Object} params {bot: {}, origin: {}}
 * @param {Function} callback
 *
 * @return {Array} point {"lat": 50.60459528287801,"lng": 2.9086303710937496 }
 */
var generate_trip = function(bot, origin, callback) {
    var latLngs = bot.zone.properties.latLngs;
    var bbox = bot.zone.properties.bbox;
    if (origin === undefined) {
      var start_points = geometry.randomPointInPolygon(latLngs, bbox);
    } else {
      var start_points = origin
    }
    var end_points = geometry.randomPointInPolygon(latLngs, bbox);

    var coords = { origin: start_points, destination: end_points };
    trip.generate_directions(coords, function(directions){
      // trip.generate_road(directions, callback);

      var trip = geometry.splitTrip(directions, 20);
      callback(trip);
    });
  };

var generate_directions = function(coords, callback) {
    googleMapsClient.directions(coords, function(err, response) {
      if (err) {
        console.log(err)
        return false; // Should also return err
      } else {
        var steps = response.json.routes[0].legs[0].steps;
        tripPoints = [];
        steps.forEach(function(step, index) {
          var lat, lng;
          if (index == steps.length) {
            lat = step.end_location.lat;
            lng = step.end_location.lng;
          } else {
            lat = step.start_location.lat;
            lng = step.start_location.lng;
          }
          var stepLatLng = { lat: lat, lng: lng };
          tripPoints.push(stepLatLng);
        });
        callback(tripPoints);
        return true;
      }
    });
  };

var generate_road = function(directions, callback) {
    googleMapsClient.snapToRoads({path: directions, interpolate: true}, function(err, response) {
      if (err) {
        console.log(err)
        return false; // Should also return err
      } else {
        tripPoints = [];
        var steps = response.json.snappedPoints;
        steps.forEach(function(step, index) {
          var lat, lng;
          lat = step.location.latitude;
          lng = step.location.longitude;
          var stepLatLng = { lat: lat, lng: lng, placeId: step.placeId};
          tripPoints.push(stepLatLng);
        });
        callback(geometry.splitTrip(tripPoints));
        return true;
      }
    });
  }

trip = {

  tripPoints: tripPoints,

  /**
   * Generate a trip with params
   *
   * @param {Object} params {bot: {}, origin: {"lat": 50.6045,"lng": 2.9086 }}
   * @param {Function} callback
   *
   * @return {Array} points [{"lat": 50.6045,"lng": 2.9086 }, {"lat": 50.6045,"lng": 2.9086 }, {"lat": 50.6045,"lng": 2.9086 }]
   */
  generate_trip: generate_trip,

  generate_directions: generate_directions,

  generate_road: generate_road
};

module.exports = trip;
<commit_msg>Use bot speed for split trip<commit_after>var maps = require('@google/maps')
var config = require('dotenv').config();
var googleMapsClient = maps.createClient({
  key: process.env.GOOGLE_API_KEY
});

var tripPoints = [];

/**
 * Generate a trip with params
 *
 * @param {Object} params {bot: {}, origin: {}}
 * @param {Function} callback
 *
 * @return {Array} point {"lat": 50.60459528287801,"lng": 2.9086303710937496 }
 */
var generate_trip = function(bot, origin, callback) {
    var latLngs = bot.zone.properties.latLngs;
    var bbox = bot.zone.properties.bbox;
    if (origin === undefined) {
      var start_points = geometry.randomPointInPolygon(latLngs, bbox);
    } else {
      var start_points = origin
    }
    var end_points = geometry.randomPointInPolygon(latLngs, bbox);

    var coords = { origin: start_points, destination: end_points };
    trip.generate_directions(coords, function(directions) {
      // trip.generate_road(directions, callback);
      var trip = geometry.splitTrip(directions, bot.speed);
      callback(trip);
    });
  };

var generate_directions = function(coords, callback) {
    googleMapsClient.directions(coords, function(err, response) {
      if (err) {
        console.log(err)
        return false; // Should also return err
      } else {
        var steps = response.json.routes[0].legs[0].steps;
        tripPoints = [];
        steps.forEach(function(step, index) {
          var lat, lng;
          if (index == steps.length) {
            lat = step.end_location.lat;
            lng = step.end_location.lng;
          } else {
            lat = step.start_location.lat;
            lng = step.start_location.lng;
          }
          var stepLatLng = { lat: lat, lng: lng };
          tripPoints.push(stepLatLng);
        });
        callback(tripPoints);
        return true;
      }
    });
  };

var generate_road = function(directions, callback) {
    googleMapsClient.snapToRoads({path: directions, interpolate: true}, function(err, response) {
      if (err) {
        console.log(err)
        return false; // Should also return err
      } else {
        tripPoints = [];
        var steps = response.json.snappedPoints;
        steps.forEach(function(step, index) {
          var lat, lng;
          lat = step.location.latitude;
          lng = step.location.longitude;
          var stepLatLng = { lat: lat, lng: lng, placeId: step.placeId};
          tripPoints.push(stepLatLng);
        });
        callback(tripPoints);
        return true;
      }
    });
  }

trip = {

  tripPoints: tripPoints,

  /**
   * Generate a trip with params
   *
   * @param {Object} params {bot: {}, origin: {"lat": 50.6045,"lng": 2.9086 }}
   * @param {Function} callback
   *
   * @return {Array} points [{"lat": 50.6045,"lng": 2.9086 }, {"lat": 50.6045,"lng": 2.9086 }, {"lat": 50.6045,"lng": 2.9086 }]
   */
  generate_trip: generate_trip,

  generate_directions: generate_directions,

  generate_road: generate_road
};

module.exports = trip;
<|endoftext|>