#StackBounty: #javascript #selenium #selenium-webdriver #webdriver-io #firefox-profile How to use socks authentication in webdriverio

Bounty: 50

I would like to have SOCKSv5 authentication enabled in my webdriverio application. However it doesn’t seem to work at the moment.

So far i have tried the following configurations:

  1. Setting manually using firefox-profile as followed:
fp.setProxy({ 
    proxyType: 'manual', 
    socksProxy: `127.0.0.1:1080`,
    socksUsername: 'myuser',
    socksPassword: 'mypass'
});

this didn’t seem to work as pages simply don’t load.

  1. Using FoxyProxy

I have tried setting the url using its api:

proxy:host=${proxy}&port=1080&isSocks=true&enabled=true&username=myuser&password=mypass

Which gave me a warning that something is accessing its configuration and thus declining the request.

Creating a custom firefox profile and then loading that one doesn’t work as i will have to edit proxies during my applications runtime.

Has anybody came across a solution?


Get this bounty!!!

#StackBounty: #javascript #css #angularjs #angularjs-directive #angularjs-scope Angular Digest Loop on ng-style

Bounty: 50

I have a filter that changes filtered object. But when I’m using ng-style="item.gridSize"
My Filter: (The Algorithm for size Grid was taken (changed for my needs) from Here

angular.module("custom.modules.photoGrid", []).filter('photoSearch', [function () {
    function getGrid(photos){
        var output = [];
        var HEIGHTS = [];
        var COLUMN_WIDTH = 227;
        var MARGIN = 6;
        var DELTA = 20;

        var size = window.innerWidth - 50;
        var n_columns = Math.floor(size / (2 * (COLUMN_WIDTH + MARGIN)));
        create_columns(n_columns);
        var small_images = [];

        function create_columns(n) {
            HEIGHTS = [];
            for (var i = 0; i < n; ++i) {
                HEIGHTS.push(0);
            }
        }

        function get_min_column() {
            var min_height = Infinity;
            var min_i = -1;
            for (var i = 0; i < HEIGHTS.length; ++i) {
                if (HEIGHTS[i] < min_height) {
                min_height = HEIGHTS[i];
                min_i = i;
                }
            }
            return min_i;
        }

        function gridSize(i, is_big) {
            var size = {
                'margin-left': (MARGIN + (COLUMN_WIDTH + MARGIN) * i)+'px',
                'margin-top': (HEIGHTS[Math.floor(i / 2)] * (COLUMN_WIDTH + MARGIN))+'px',
                'width': is_big ? (COLUMN_WIDTH * 2 + MARGIN)+'px' : COLUMN_WIDTH+'px',
                'height': is_big ? (COLUMN_WIDTH * 2 + MARGIN)+'px' : COLUMN_WIDTH+'px'
            };
            return size;
        }
        function createGrid(data){
            if (data.length >= 2) {
                for(var i = 0; i < data.length; i++){
                    var column = get_min_column();
                    if (Math.random() > 0.8) {
                        data[i]['gridSize'] = gridSize(column * 2, true);
                        HEIGHTS[column] += 2;
                    } else {
                        small_images.push(i);
                        if (small_images.length === 2) {
                            data[small_images[0]]['gridSize'] = gridSize(column * 2, false);
                            data[small_images[1]]['gridSize'] = gridSize(column * 2 + 1, false);
                            HEIGHTS[column] += 1;
                            small_images = [];
                        }
                    }
                }
                if (small_images.length) {
                    column = get_min_column();
                    data[(data.length-1)]['gridSize'] = gridSize(column * 2, false);
                }
            }

            return data;
        }
        var grid = createGrid(photos);
        return grid;
    }


    return function(photos, search) {
        var filtered = [];
        if(!!search){ /**@case1 if only search query is present**/
            search = search.toLowerCase();
            for(var i = 0; i < photos.length; i++){
                if(photos[i].photo_name.toLowerCase().indexOf(search) !== -1){
                    filtered.push(photos[i]);
                }
            }

        }else {
            /**@case2 no query is present**/
            filtered = photos;
        }
        filtered = getGrid(filtered);
        return filtered;
    }
}]);

Html:

<input type="text" ng-model="input.value"> <span>{{ results.length }}</span> Photo Found

A small explanation:
Every time ng-model input.value changed filter is runed and creates different grid for filtered array of photos. all dimensions are written inside gridSize and this cause digest loop.

What I’ve tried until now: I’ve moved my ng-repeat in directive, but this way I can’t access result.length and input.value.

I’ve also tried a bindonce directive but using it like bo-style="photo.gridSize" doesn’t change the grid after user search(and is logically right because is bidden only once, but values changed.

So my question is how to make ng-repeat assign new grdiSize property without running in digest loop.

UPDATE: JSFiddle


Get this bounty!!!

#StackBounty: #2013 #javascript #jquery #jslink #client-side-rendering Unable to write a jslink which defines a default values for my D…

Bounty: 100

I am working on a list view, where i have a choice column named TrackingStatus. Now for this choice column i defined the default value to be In Progress from the site columns settings.

But i have noticed that inside the Quick edit grid the default value for the TrackingStatus column will not be rendered (unlike inside the built-in create and edit forms).. so i tried to implement the default values using JS Link as follow:

I upload the following script inside my site collection:

(function () {
    alert("1");
    var overrideContext= {};

    overrideContext.Templates = overrideContext.Templates || {};

    overrideContext.Templates.Fields = {
        "TrackingStatus": {
            "View": function (ctx) {if(ctx.CurrentItem.your_column == "")
        return "In Progress";
}
        }
    };

    SPClientTemplates.TemplateManager.RegisterTemplateOverrides(overrideContext);

})();

Then I edit my list view WebPart and I provide the following to reference the above JSlink under the WebPart Miscellaneous section.:

~sitecollection/Resources/defaultvalues.js

But the JS Link is not having any effect in respect to applying default values for my column, although when I checked the page source I can see that the defaultvalues.js is being loaded and i got the alert(1) which I intentionally added inside the JS Link.

How can I define default values for my choices columns inside the Quick Edit grid?


Get this bounty!!!

#StackBounty: #javascript #reactjs #webpack #jestjs #webpack-2 Testing with Jest and Webpack aliases

Bounty: 200

I am looking to be able to use webpack aliases to resolve imports when using jest, and optimally, reference the webpack.aliases to avoid duplication.

Jest conf:

  "jest": {
    "modulePaths": ["src"],
    "moduleDirectories": ["node_modules"],
    "moduleNameMapper": {
      "^@shared$": "<rootDir>/shared/",
      "^@components$": "<rootDir>/shared/components/"
    }
  },

Webpack aliases:

exports.aliases = {
    '@shared': path.resolve(paths.APP_DIR, 'shared'),
    '@components': path.resolve(paths.APP_DIR, 'shared/components'),
};

Imports:

import Ordinal from '@shared/utils/Ordinal.jsx';
import Avatar from '@components/common/Avatar.jsx';

For some reason the @ causes issues, so when removed (in both alias and import), it can find shared but components still cannot be resolved.

 FAIL  src/shared/components/test/Test.spec.jsx
  ● Test suite failed to run

    Cannot find module '@shared/utils/Ordinal.jsx' from 'Test.jsx'

I have tried using jest-webpack-alias, babel-plugin-module-resolver and the Jest/Webpack docs


Get this bounty!!!

#StackBounty: #javascript #node.js #asynchronous #ecmascript-6 #promise Node.js module using Promises (client for KeePassHttp)

Bounty: 50

This is my first Node module, as well as the first time using Promises in Javascript. It is a client for the KeePass plugin “KeePassHTTP” to expose passwords securely, which I am planning on using to pass credentials to gulp.

All feedback welcome!

// Code based on https://github.com/belaviyo/keepass-macpass-helper
// Keepass HTTP protocol documentation - https://github.com/pfn/keepasshttp

const sjcl = require('./sjcl').sjcl;                     // not using npm module because it doesn't have support for cbc
const nconf = require('nconf');
const rp = require('request-promise-native');

let port = null;
let key = null;
let id = null;

function iv(len = 16) {
  let iv = [];
  for (let i = 0; i < len; i++) {
    iv.push(String.fromCharCode(Math.floor(Math.random() * 256)));
  }
  return new Buffer(iv.join(''), 'binary').toString('base64');
}

function encrypt(data, iv) {
  const enc = sjcl.mode.cbc.encrypt(
      new sjcl.cipher.aes(sjcl.codec.base64.toBits(key)),
      sjcl.codec.utf8String.toBits(data),
      sjcl.codec.base64.toBits(iv)
  );
  return sjcl.codec.base64.fromBits(enc);
}

function decrypt(data, iv) {
  const dec = sjcl.mode.cbc.decrypt(
      new sjcl.cipher.aes(sjcl.codec.base64.toBits(key)),
      sjcl.codec.base64.toBits(data),
      sjcl.codec.base64.toBits(iv)
  );
  return sjcl.codec.utf8String.fromBits(dec);
}

function verify(request) {
  const nonce = iv();
  request['Nonce'] = nonce;
  request['Verifier'] = encrypt(nonce, nonce);
  if (id) {
    request['Id'] = id;
  }
  return request;
}

function post(request) {
  return rp({
    method: 'POST',
    uri: `http://localhost:${port}`,
    body: request,
    json: true
  });
}

function init() {
  return new Promise(function(resolve) {
    nconf.file({file: '.keepass'});

    nconf.defaults({
      port: 19455
    });

    port = nconf.get('port');
    key = nconf.get('key');
    id = nconf.get('id');

    if (!key) {
      key = iv(32);
      nconf.set('key', key);
      nconf.save();
    }
    resolve();
  });
}

function test() {
  return new Promise((resolve, reject) => {
    let request = {
      RequestType: 'test-associate',
      TriggerUnlock: false,
    };
    request = verify(request);
    post(request)
        .then(response => {
          if (response && response['Success']) {
            resolve(response);
          } else {
            reject(response);
          }
        })
        .catch(response => reject(response));
  });
}

function associate() {
  return new Promise((resolve, reject) => {
    let request = {
      RequestType: 'associate',
      Key: key
    };
    request = verify(request);
    post(request)
        .then(response => {
          if (response && response['Success']) {
            id = response['Id'];
            nconf.set('id', id);
            nconf.save();
            resolve(response);
          } else {
            reject(response);
          }
        })
        .catch(response => reject(response));
  });
}

function logins(url) {
  return new Promise((resolve, reject) => {
    let request = {
      RequestType: 'get-logins',
      TriggerUnlock: 'false',
      SortSelection: 'false',
    };
    request = verify(request);
    const iv = request['Nonce'];
    request['Url'] = encrypt(url, iv);

    post(request)
        .then(response => {
          if (response && response['Entries']) {
            let nonce = response.Nonce;
            response.Entries = response.Entries.map(entry => {
              return Object.assign(entry, {
                Login: decrypt(entry.Login, nonce),
                Name: decrypt(entry.Name, nonce),
                Password: decrypt(entry.Password, nonce)
              })
            });
            resolve(response);
          } else {
            reject(response);
          }
        })
        .catch(response => reject(response));
  });
}

// itl: init -> test -> logins

function itl(url) {
  return new Promise((resolve, reject) => {
    init()
        .then(() => test())
        .then(() => logins(url))
        .then(response => resolve(response))
        .catch(() => {
          associate()
              .then(() => logins(url))
              .then(response => resolve(response))
              .catch(response => reject(response));
        });
  });
}

module.exports = {
  init,
  test,
  associate,
  logins,
  itl,
};

Example Usage:

const keepass = require('./keepass/keepass.js');

keepass.itl('www.example.com')
    .then(result => console.log('Success', result))
    .catch(result => console.log('Error', result))


Get this bounty!!!

#StackBounty: #javascript #php #post #http-post Get bytes transferred using PHP5 for POST request

Bounty: 50

Note I am new to PHP, Apache and programming for servers so more thorough explanations will be appreciated.


Context

I created – in javascript – a progress bar to display when a file is uploaded. Current I have the progress bar update at a set frame-rate (to see if it works).

Clearly to make this an accurate progress bar, everything should in relation to the number of bytes transferred in comparison to the total number of bytes.

Question

using PHP5 how can I get information regarding the number of bytes transferred in relation to the total number of bytes of the file, such that I can pass that to a JS function updateProgress(bytesSoFar, totalBytes) to update my progress bar? Please verbosely walk me through the modifications needed to the code below to get this to work. I have seen xhr examples, but they are not thoroughly accessible.

I have just set up LocalHost and am using W3Schools’ PHP File Upload tutorial. To get the simulated ”upload” to work, I changed the local permissions as suggested in this S.O. post. I don’t necessarily need to read the file, I just want to know many bytes have been transferred.


Code

Currently I have two files:

  • index.php

  • upload.php

index.php

<!DOCTYPE html>
<html>
<body>

<form action="upload.php" method="post" enctype="multipart/form-data">
    Select image to upload:
    <input type="file" name="fileToUpload" id="fileToUpload">
    <input type="submit" value="Upload Image" name="submit">
</form>

</body>
</html>

upload.php

<?php
$target_dir = "uploads/";
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = pathinfo($target_file,PATHINFO_EXTENSION);
// Check if image file is a actual image or fake image
if(isset($_POST["submit"])) {
    $check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
    if($check !== false) {
        echo "File is an image - " . $check["mime"] . ".";
        $uploadOk = 1;
    } else {
        echo "File is not an image.";
        $uploadOk = 0;
    }
}
// Check if file already exists
if (file_exists($target_file)) {
    echo "Sorry, file already exists.";
    $uploadOk = 0;
}
// Check file size
if ($_FILES["fileToUpload"]["size"] > 500000) {
    echo "Sorry, your file is too large.";
    $uploadOk = 0;
}
// Allow certain file formats
if($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
&& $imageFileType != "gif" ) {
    echo "Sorry, only JPG, JPEG, PNG & GIF files are allowed.";
    $uploadOk = 0;
}
// Check if $uploadOk is set to 0 by an error
if ($uploadOk == 0) {
    echo "Sorry, your file was not uploaded.";
// if everything is ok, try to upload file
} else {
    if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
        echo "The file ". basename( $_FILES["fileToUpload"]["name"]). " has been uploaded.";
    } else {
        echo "Sorry, there was an error uploading your file.";
    }
}
?>


Get this bounty!!!

#StackBounty: #javascript #3d #three.js #rotation Find angle to rotate point so it faces another point in 3d space

Bounty: 250

Say I have two vectors:

V1 = { x: 3.296372727813439, y: -14.497928014719344, z: 12.004105246875968 }

V2 = { x: 2.3652551657790695, y: -16.732085083053185, z: 8.945905454164146 }

How can I figure out what angle v1 needs to be rotated to look directly at v2?

Put into English: say I knew exactly where I was in space, and exactly where another person was somewhere else in space…. Mathematically, how could I figure out what angles to put my finger at to point at them?

Here’s what my axis looks like

My current (incorrect) formula

v2.x -= v1.x; // move v2 (vector 2) relative to the new origin
v2.y -= v1.y;
v2.z -= v1.z;

v1.x = 0; // set v1 (vector 1) as the origin
v1.y = 0;
v1.z = 0;

var r = Math.sqrt(Math.pow(v2.x,2) + Math.pow(v2.y,2) + Math.pow(v2.z,2));
var θ = Math.acos((Math.pow(v2.x,2) + Math.pow(v2.z,2))/(r*Math.sqrt(Math.pow(v2.x,2) + Math.pow(v2.z,2))));
var ϕ = Math.acos(v2.x/Math.sqrt(Math.pow(v2.x,2) + Math.pow(v2.z,2)));

Then I rotate v1 with the theta and phi.

v1.rotation.y = θ;
v2.rotation.x = ϕ;

But this is giving me the wrong rotation.

θ = 0.6099683401012933

ϕ = 1.8663452274936656

But if I use THREE.js and use the lookAt function, it spits out these rotations instead, that work beautifully:

y/θ: -0.24106818240525682

x/ϕ: 2.5106584861123644

Thanks for all the help in advance! I cannot use THREE.js in my final result, I’m trying to go pure vanilla JS so I can port it to another language.


Get this bounty!!!

#StackBounty: #2013 #javascript #deployment #sharepoint-hosted-app #splist Prevent Visual Studio from Overriding Poject internal SPList

Bounty: 50

Is there a way to do it?

And how can I reuse a already created List inside an App? The Lists is not accessible after a new deployment of the app. I think it is because of the new App URL.

I need help cause I dont want to input all of the data once more…


Get this bounty!!!

#StackBounty: #javascript #jquery #html #image How to check width and height from all images has been called by jquery?

Bounty: 50

i have a problem on jquery image, which i need validate all image has been clicked or show with jquery

here is my code, how i add new images and show the images


true_image = false;
var _URL = window.URL || window.webkitURL;
$(document).ready(function(){

    var abc = 0;
    var wrapper         = $(".images");
    var add_button      = $("#add_more");

    var x = 1;
    $(add_button).click(function(){

        x++;
            $(wrapper).append('
" class="remove_field">
'); }); $(wrapper).on("click",".remove_field", function(e){ e.preventDefault(); $(this).parent('div').remove(); x--; }); $('body').on('change', '#file_input', function() { var image ; if (this.files && this.files[0]) { image = new Image; image.onload =function() { console.log("width : "+this.width+" height: "+this.height); if(this.width
"); var reader = new FileReader(); reader.onload = imageIsLoaded; reader.readAsDataURL(this.files[0]); $(this).hide(); $("#abcd" + abc).append($("", { id: 'img', src: '', alt: 'delete' }).click(function() { $(this).parent().parent().remove(); if(($('#file_input').val()) == undefined){ $(wrapper).append('
'); } })); } } }); function imageIsLoaded(e) { $('#previewimg' + abc).attr('src', e.target.result); $('.remove_field').hide(); }; $('body').on('click', '.images_view', function() { abc += 1; var cover = false; var image_id = this.id; $('.images_view').filter(function( index ) { if($( this ).attr( "id" ) === image_id){ $(this).parent().find('#file_input').attr('name','cover'); $(this).parent().find('#cover').remove(); $(this).parent().append("Cover"); }else{ $(this).parent().find('#file_input').attr('name','file[]'); $(this).parent().find('#cover').remove(); } }) }); });

from the above code, i trying to upload multi images so i add a button, and after it was clicked a new <input id="file_input" class="images_file" type="file" name="file[]"/> will be added, than after an images has been selected it could show the images. i want trying to validate the file is an image file, width and height on the image

my problem are, if i selected the false image (not in criteria) the button will be disabled but if in the next select, i choose the true image the button back to normal. I know this will happen because i don’t check the old images that has been selected before, and that is my problem i don’t know how to check it.

guys can you help me how to solve the problem?


Get this bounty!!!

#StackBounty: #javascript #performance #object-oriented #node.js #database Making job listings project more modular and flexible

Bounty: 50

I am creating a pet project where candidates can apply and recruiters can post their listings:

db.js

var pg = require('pg');

var config = {
  host: 'localhost',
  user: 'v',
  password: 'a',
  database: 'j',
};

var pool = new pg.Pool(config);

pool.connect(function(err, client, done) {
  if(err) {
    return console.error('error fetching client from pool', err);
  }
  client.query('SELECT $1::int AS number', ['1'], function(err, result) {
    done(err);

    if(err) {
      return console.error('error running query', err);
    }
    console.log(result.rows[0].number);
    //output: 1
  });
});

pool.on('error', function (err, client) {
  // if an error is encountered by a client while it sits idle in the pool
  // the pool itself will emit an error event with both the error and
  // the client which emitted the original error
  // this is a rare occurrence but can happen if there is a network partition
  // between your application and the database, the database restarts, etc.
  // and so you might want to handle it and at least log it out
  console.error('idle client error', err.message, err.stack)
});

module.exports = pool;

index.js

'use strict';

var express = require('express');
var Promise = require('promise');
var app = express();
var router = express.Router();
var PORT = 3000;
var pool = require('./db.js');

app.use(function (req, res, next) {
  pool.connect(function(error, client, done) {
    // Handle connection errors
    if(error) {
      done();
      console.log(error.message);
      return res.status(500).json({success: false, data: error});
    }
    req.client = client;
    next();
  });
});

router.get('/topActiveUsers', (req, res) => {
  topActiveUsers(req, res);
});

router.get('/users', (req, res) => {
  userInfo(req, res);
});

app.use(router);

app.get('*', function (req, res) {
  res.status(400).send('Invalid route');
});

app.listen(PORT, function () {
  console.log('App listening on port ' + PORT);
});

var topActiveUsers = function topActiveUsers(req, res) {
};

var userInfo = function userInfo(req, res) {
  User.getById(req)
      .then(function (user) {
        return user;
      })
      .then(function getCompanies(user) {
        return user.companies(req);
      })
      .then(function getListings(user) {
        return user.listings(req);
      })
      .then(function getApplications(user) {
        return user.applications(req);
      })
      .then(function success(user) {
        res.json({
          id: user.id,
          name: user.name,
          createdAt: user.createdAt,
          companies: user._companies,
          listings: user._listings,
          applications: user._applications
        });
      })
      .catch(function error(error) {
        console.log('In catch');
        console.log('error', error.message);
        res.end();
        throw error;
      });
};

/**
 * User m2m Company
 * User o2m Listing
 * User m2m applications
 */
function User(opt_data) {
  var data = opt_data || {};

  this.id = data['id'] || null;
  this.name = data['name'] || '';
  this.createdAt = data['created_at'] || new Date();
  this._companies = [];
  this._listings = [];
  this._applications = [];
}
User._RESOURCE_LIMIT = 5;
User._TABLE_NAME = 'users';

User.getById = function getById(req, callback) {
  var client = req.client;
  client.connection.on('message', function(msg) {
    console.log(msg)
    //console.log(msg.name)
  });
  var queryString = 'select * from users where id = $1::int';
  return new Promise(function _promise(resolve, reject) {
    client.query(
        queryString, [req.query.id],
        function result(error, result) {
      if (error) {
        console.log(error.message);
        return reject(error);
      }

      //console.log(result.rows);

      /*
      client.end(function (error) {
        if (error) throw error;
      });*/
      resolve(new User(result.rows[0]));
    });
  });
};

var UserProto = User.prototype;

UserProto.companies = function companies(req) {
  var client = req.client;
  var queryString = 'select c.id, c.name, t.contact_user '+
    'from companies c, teams t '+
    'where t.user_id = $1::int and t.company_id = c.id '+
    'limit $2::int';
  var self = this;
  return new Promise(function _promise(resolve, reject) {
    client.query(
        queryString,
        [req.query.id, User._RESOURCE_LIMIT],
        function result(error, result) {
      if (error) return reject(error);

      //console.log(result.rows);

      /*
      client.end(function (error) {
        if (error) throw error;
      });*/
      self._companies = result.rows.map(function (data) {
        return new Company(data);
      });
      resolve(self);
    });
  });
};

UserProto.listings = function listings(req, callback) {
  var client = req.client;
  var queryString = 'select * from listings '+
    'where created_by = $1::int '+
    'limit $2::int';
  var self = this;
  return new Promise(function _promise(resolve, reject) {
    client.query(
        queryString,
        [req.query.id, User._RESOURCE_LIMIT],
        function result(error, result) {
      if (error) {
        return reject(error);
      }

      //console.log(result.rows);

      /*
      client.end(function (error) {
        if (error) throw error;
      });*/
      self._listings = result.rows.map(function (data) {
        return new Listing(data);
      });
      //console.log('Got listings', self);
      resolve(self);
    });
  });
};

UserProto.applications = function applications(req, callback) {
  var client = req.client;
  var queryString = 'select a.id as app_id, a.created_at, a.cover_letter, '+
    'l.id as list_id, l.name, l.description '+
    'from applications a, listings l '+
    'where a.user_id = $1::int and a.listing_id = l.id '+
    'limit $2::int';
  var self = this;
  return new Promise(function _promise(resolve, reject) {
    client.query(
        queryString,
        [req.query.id, User._RESOURCE_LIMIT],
        function result(error, result) {
      if (error) return reject(error);

      console.log(result.rows);

      /*
      client.end(function (error) {
        if (error) throw error;
      });*/
      self._applications = result.rows.map(function (data) {
        return new Application(data);
      });
      resolve(self);
    });
  });
};

function Company(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.name = data['name'] || '';
  this.isContact = false;
}

function Listing(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.name = data['name'] || '';
  this.description = data['description'] || '';
}

function Application(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.listing = data['listing'] || null;
  this.coverLetter = data['cover_letter'] || '';
}

data.sql

insert into users (id, created_at, name) values
    (1, '2015-01-13 15:30', 'Mark'),
    (2, '2015-01-13 15:30', 'John'),
    (3, '2016-01-01 10:30', 'Melinda'),
    (4, '2016-01-17 23:30', 'Carl'),
    (5, '2016-02-02 16:30', 'Tim'),
    (6, '2016-02-02 16:30', 'Jessica')
;

insert into companies (id, created_at, name) values
    (1, '2015-01-13 15:00', 'Facewall'),
    (2, '2015-01-17 15:00', 'Carl & Co')
;

insert into teams (company_id, user_id, contact_user) values
    (1, 1, TRUE),
    (2, 3, FALSE),
    (2, 4, TRUE)
;

insert into listings (id, created_at, created_by, name, description) values
    (1, '2015-01-15 11:00', 1, 'Join us conquering the world!', 'This is your best chance to be on the right side of the equation...')
;

insert into applications (created_at, user_id, listing_id, cover_letter) values
    ('2015-01-16 12:00', 2, 1, 'Hello, ...')
;

Notes:

  1. I need to apply dependency injection from start. I don’t have enough experience in NodeJS but I have tried one approach here. I would like to know if there is a better approach.
  2. Is the code flexible and maintainable? I see some repetitive logic. How can I refactor it?
  3. Can my code handle thousands of requests? If not, what changes do I need to make?
  4. What if I give this code as part of an interview process? What are the points where I can get rejected?

Update

Here is my updated code:

index.js

'use strict';

var express = require('express');
var Promise = require('promise');
var router = express.Router();
var app = express();

var pool = require('./db.js')();
var User = require('./models');

var PORT = 3000;

app.use(function (req, res, next) {
  pool.connect(function(error, client, done) {
    // Handle connection errors
    if (error) {
      done(error);
      console.log(error.message);
      return res.status(500)
          .json({success: false, data: error});
    }
    req.client = client;
    req.done = done;
    next();
  });
});
**index.js**

'use strict';

var express = require('express');
var Promise = require('promise');
var router = express.Router();
var app = express();

var pool = require('./db.js')();
var User = require('./models');

var PORT = 3000;

app.use(function (req, res, next) {
  pool.connect(function(error, client, done) {
    // Handle connection errors
    if (error) {
      done(error);
      console.log(error.message);
      return res.status(500)
          .json({success: false, data: error});
    }
    req.client = client;
    req.done = done;
    next();
  });
});

router.get('/topActiveUsers', (req, res) => {
  topActiveUsers(req, res);
});

router.get('/users', (req, res) => {
  userInfo(req, res);
});

app.use(router);

app.get('*', function (req, res) {
  res.status(400).send('Invalid route');
});

app.listen(PORT, function () {
  console.log('App listening on port ' + PORT);
});

var topActiveUsers = function topActiveUsers(req, res) {
  var ENTRIES_PER_PAGE = 3;
  var startIndex = 0;
  var total = 0;
  req.query.page = +req.query.page || 0;

  var pageNum = req.query.page > 0 ? req.query.page : 0;
  if (pageNum > 0) {
    startIndex = ENTRIES_PER_PAGE * (pageNum - 1);
  }
  total = ENTRIES_PER_PAGE * (pageNum + 1);

  User.topActiveUsers(req)
      .then(function fullfilled(users) {
        if (users.length < startIndex) {
          throw new Error('Invalid pagination offset');
        }
        if (users.length > total) {
          users = users.slice(startIndex, startIndex + ENTRIES_PER_PAGE);
        } else {
          users = users.splice(startIndex);
        }
        return Promise.all(users.map(function (user) {
          return user.applicationListings(req);
        }));
      })
      .then(function fullfilled(users) {
        var result = users.map(function (user) {
          return {
            id: user.id,
            name: user.name,
            count: user._appliedListings.length,
            createdAt: user.createdAt,
            listings: user._appliedListings
          };
        });
        res.json(result);
      })
      .catch(function rejected(error) {
        console.log(error.message);
        throw error;
      })
      .finally(function () {
        res.end();
      });
};

var userInfo = function userInfo(req, res) {
  User.getById(req)
      // run companies/listings/applications in "parallel"
      .then(function fullfilled(user) {
        return Promise.all([
          user.id,
          user.name,
          user.createdAt,
          user.companies(req),
          user.listings(req),
          user.applications(req)
        ]);
      })
      .then(function fullfilled([
            id, name, createdAt, companies, listings, applications]) {
        res.json({
          id: id,
          name: name,
          createdAt: createdAt,
          companies: companies,
          listings: listings,
          applications: applications
        });
      })
      .catch(function rejected(error) {
        console.log('error', error.message);
        throw error;
      })
      .finally(function () {
        res.end();
      });
};

models/index.js

var Promise = require('promise');

module.exports = User;

/**
 * User m2m Company
 * User o2m Listing
 * User m2m applications
 */
function User(opt_data) {
  var data = opt_data || {};

  this.id = data['id'] || null;
  this.name = data['name'] || '';
  this.createdAt = data['created_at'] || new Date();
  this._companies = [];
  this._listings = [];
  this._applications = [];
  this._appliedListings = [];
}
User._RESOURCE_LIMIT = 5;
var UserProto = User.prototype;

User.topActiveUsers = function topActiveUsers(req) {
  var queryString = "select * from users u inner join "+
      "(select user_id, count(id) cnt from applications "+
      "where id in (select id from applications where "+
      "created_at > current_date - interval '1 week') "+
      "group by user_id) a on u.id = a.user_id order by a.cnt desc";
  return queryPromise(req, queryString)
      .then(function fullfilled(result) {
        return result.rows.map(function(row) {
          return new User(row);
        });
      });
};

User.getById = function getById(req) {
  var queryString = 'select * from users where id = $1::int';
  return queryPromise(req, queryString, [req.query.id])
      .then(function fullfilled(result) {
        return new User(result.rows[0]);
      });
};

UserProto.companies = function companies(req) {
  var queryString = 'select c.id, c.name, t.contact_user '+
      'from companies c, teams t '+
      'where t.user_id = $1::int and t.company_id = c.id '+
      'limit $2::int';
  return queryPromise(req, queryString, [this.id, User._RESOURCE_LIMIT])
    .then(function fullfilled(result) {
      return result.rows.map(function (data) {
        return new Company(data);
      });
    });
};

UserProto.listings = function listings(req) {
  var queryString = 'select * from listings '+
      'where created_by = $1::int '+
      'limit $2::int';
  return queryPromise(req, queryString, [this.id, User._RESOURCE_LIMIT])
    .then(function fullfilled(result) {
      return result.rows.map(function (data) {
        return new Listing(data);
      });
    });
};

UserProto.applicationListings = function applications(req) {
  var queryString = "select * from listings l inner join "+
      "(select listing_id, user_id, created_at from applications) a "+
      "on a.listing_id = l.id "+
      "where a.user_id = $1::int order by a.created_at desc limit 3";
  var self = this;
  return queryPromise(req, queryString, [this.id])
      .then(function fullfilled(result) {
        self._appliedListings = result.rows.map(function (data) {
          return new Listing(data);
        });
        return self;
      });
};

UserProto.applications = function applications(req) {
  var queryString = 'select a.id as app_id, a.created_at, a.cover_letter, '+
    'l.id as list_id, l.name, l.description '+
    'from applications a, listings l '+
    'where a.user_id = $1::int and a.listing_id = l.id '+
    'limit $2::int';
  return queryPromise(req, queryString, [this.id, User._RESOURCE_LIMIT])
      .then(function fullfilled(result) {
        return result.rows.map(function (data) {
          return new Application(data);
        });
      });
};

function Company(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.name = data['name'] || '';
  this.isContact = false;
}

function Listing(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.name = data['name'] || '';
  this.description = data['description'] || '';
}

function Application(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.listing = data['listing'] || null;
  this.coverLetter = data['cover_letter'] || '';
}

function queryPromise(req, queryString, queryParams, debug) {
  if (debug) {
    console.log(queryString, queryParams);
    req.client.connection.on('message', function(msg) {
      console.log(msg)
    });
  }
  return new Promise(function _promise(resolve, reject) {
    req.client.query(
        queryString,
        queryParams || [],
        function result(error, result) {
      req.done(error);

      if (error) {
        console.log('error ' + error.message);
        return reject(error);
      }
      resolve(result);
    });
  });
};

db.js

var pg = require('pg');

module.exports = function() {
  var config = {
    port: 5432,
    max: 10,
    idleTimeoutMillis: 30000
  };
  switch (process.env.NODE_ENV) {
    case 'development':
      config.host = 'localhost';
      config.user = 'xxxx';
      config.password = 'xxxx';
      config.database = 'xxxx';
      break;
    case 'production':
      config.user = 'xxxx';
      config.database = 'xxxx';
      config.password = 'xxxx';
      config.host = 'xxxx'
      break;
    default:
      throw new Error('Invalid enviroment');
  }

  var pool = new pg.Pool(config);

  pool.connect(function(err, client, done) {
    if(err) {
      return console.error('error fetching client from pool', err);
    }
    client.query('SELECT $1::int AS number', ['1'], function(err, result) {
      done(err);

      if(err) {
        return console.error('error running query', err);
      }
      console.log(result.rows[0].number);
      //output: 1
    });
  });

  pool.on('error', function (err, client) {
    // if an error is encountered by a client while it sits idle in the pool
    // the pool itself will emit an error event with both the error and
    // the client which emitted the original error
    // this is a rare occurrence but can happen if there is a network partition
    // between your application and the database, the database restarts, etc.
    // and so you might want to handle it and at least log it out
    console.error('idle client error', err.message, err.stack)
  });

  return pool;
};

tables.sql

create table users (
    id serial primary key,
    created_at timestamp default current_timestamp,
    name character varying(64)
);

create table companies (
    id serial primary key,
    created_at timestamp default current_timestamp,
    name character varying(64)
);

create table teams (
    id serial primary key,
    company_id integer references companies (id),
    user_id integer references users (id),
    contact_user boolean default false
);

create table listings (
    id serial primary key,
    created_at timestamp default current_timestamp,
    created_by integer references users (id),
    name character varying(64),
    description text
);

create table applications (
    id serial primary key,
    created_at timestamp default current_timestamp,
    user_id integer references users (id),
    listing_id integer references listings (id),
    cover_letter text
);
router.get('/topActiveUsers', (req, res) => {
  topActiveUsers(req, res);
});

router.get('/users', (req, res) => {
  userInfo(req, res);
});

app.use(router);

app.get('*', function (req, res) {
  res.status(400).send('Invalid route');
});

app.listen(PORT, function () {
  console.log('App listening on port ' + PORT);
});

var topActiveUsers = function topActiveUsers(req, res) {
  var ENTRIES_PER_PAGE = 3;
  var startIndex = 0;
  var total = 0;
  req.query.page = +req.query.page || 0;

  var pageNum = req.query.page > 0 ? req.query.page : 0;
  if (pageNum > 0) {
    startIndex = ENTRIES_PER_PAGE * (pageNum - 1);
  }
  total = ENTRIES_PER_PAGE * (pageNum + 1);

  User.topActiveUsers(req)
      .then(function fullfilled(users) {
        if (users.length < startIndex) {
          throw new Error('Invalid pagination offset');
        }
        if (users.length > total) {
          users = users.slice(startIndex, startIndex + ENTRIES_PER_PAGE);
        } else {
          users = users.splice(startIndex);
        }
        return Promise.all(users.map(function (user) {
          return user.applicationListings(req);
        }));
      })
      .then(function fullfilled(users) {
        var result = users.map(function (user) {
          return {
            id: user.id,
            name: user.name,
            count: user._appliedListings.length,
            createdAt: user.createdAt,
            listings: user._appliedListings
          };
        });
        res.json(result);
      })
      .catch(function rejected(error) {
        console.log(error.message);
        throw error;
      })
      .finally(function () {
        res.end();
      });
};

var userInfo = function userInfo(req, res) {
  User.getById(req)
      // run companies/listings/applications in "parallel"
      .then(function fullfilled(user) {
        return Promise.all([
          user.id,
          user.name,
          user.createdAt,
          user.companies(req),
          user.listings(req),
          user.applications(req)
        ]);
      })
      .then(function fullfilled([
            id, name, createdAt, companies, listings, applications]) {
        res.json({
          id: id,
          name: name,
          createdAt: createdAt,
          companies: companies,
          listings: listings,
          applications: applications
        });
      })
      .catch(function rejected(error) {
        console.log('error', error.message);
        throw error;
      })
      .finally(function () {
        res.end();
      });
};

models/index.js

var Promise = require('promise');

module.exports = User;

/**
 * User m2m Company
 * User o2m Listing
 * User m2m applications
 */
function User(opt_data) {
  var data = opt_data || {};

  this.id = data['id'] || null;
  this.name = data['name'] || '';
  this.createdAt = data['created_at'] || new Date();
  this._companies = [];
  this._listings = [];
  this._applications = [];
  this._appliedListings = [];
}
User._RESOURCE_LIMIT = 5;
var UserProto = User.prototype;

User.topActiveUsers = function topActiveUsers(req) {
  var queryString = "select * from users u inner join "+
      "(select user_id, count(id) cnt from applications "+
      "where id in (select id from applications where "+
      "created_at > current_date - interval '1 week') "+
      "group by user_id) a on u.id = a.user_id order by a.cnt desc";
  return queryPromise(req, queryString)
      .then(function fullfilled(result) {
        return result.rows.map(function(row) {
          return new User(row);
        });
      });
};

User.getById = function getById(req) {
  var queryString = 'select * from users where id = $1::int';
  return queryPromise(req, queryString, [req.query.id])
      .then(function fullfilled(result) {
        return new User(result.rows[0]);
      });
};

UserProto.companies = function companies(req) {
  var queryString = 'select c.id, c.name, t.contact_user '+
      'from companies c, teams t '+
      'where t.user_id = $1::int and t.company_id = c.id '+
      'limit $2::int';
  return queryPromise(req, queryString, [this.id, User._RESOURCE_LIMIT])
    .then(function fullfilled(result) {
      return result.rows.map(function (data) {
        return new Company(data);
      });
    });
};

UserProto.listings = function listings(req) {
  var queryString = 'select * from listings '+
      'where created_by = $1::int '+
      'limit $2::int';
  return queryPromise(req, queryString, [this.id, User._RESOURCE_LIMIT])
    .then(function fullfilled(result) {
      return result.rows.map(function (data) {
        return new Listing(data);
      });
    });
};

UserProto.applicationListings = function applications(req) {
  var queryString = "select * from listings l inner join "+
      "(select listing_id, user_id, created_at from applications) a "+
      "on a.listing_id = l.id "+
      "where a.user_id = $1::int order by a.created_at desc limit 3";
  var self = this;
  return queryPromise(req, queryString, [this.id])
      .then(function fullfilled(result) {
        self._appliedListings = result.rows.map(function (data) {
          return new Listing(data);
        });
        return self;
      });
};

UserProto.applications = function applications(req) {
  var queryString = 'select a.id as app_id, a.created_at, a.cover_letter, '+
    'l.id as list_id, l.name, l.description '+
    'from applications a, listings l '+
    'where a.user_id = $1::int and a.listing_id = l.id '+
    'limit $2::int';
  return queryPromise(req, queryString, [this.id, User._RESOURCE_LIMIT])
      .then(function fullfilled(result) {
        return result.rows.map(function (data) {
          return new Application(data);
        });
      });
};

function Company(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.name = data['name'] || '';
  this.isContact = false;
}

function Listing(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.name = data['name'] || '';
  this.description = data['description'] || '';
}

function Application(opt_data) {
  var data = opt_data || {};
  this.id = data['id'] || null;
  this.createdAt = data['created_at'] || new Date();
  this.listing = data['listing'] || null;
  this.coverLetter = data['cover_letter'] || '';
}

function queryPromise(req, queryString, queryParams, debug) {
  if (debug) {
    console.log(queryString, queryParams);
    req.client.connection.on('message', function(msg) {
      console.log(msg)
    });
  }
  return new Promise(function _promise(resolve, reject) {
    req.client.query(
        queryString,
        queryParams || [],
        function result(error, result) {
      req.done(error);

      if (error) {
        console.log('error ' + error.message);
        return reject(error);
      }
      resolve(result);
    });
  });
};

db.js

var pg = require('pg');

module.exports = function() {
  var config = {
    port: 5432,
    max: 10,
    idleTimeoutMillis: 30000
  };
  switch (process.env.NODE_ENV) {
    case 'development':
      config.host = 'localhost';
      config.user = 'vivek';
      config.password = 'admin';
      config.database = 'jobbatical';
      break;
    case 'production':
      config.user = 'rragdkrc37';
      config.database = 'rragdkrc37_db';
      config.password = 'cxbrqnn8wp';
      config.host = 'assignment.codsssqklool.eu-central-1.rds.amazonaws.com'
      break;
    default:
      throw new Error('Invalid enviroment');
  }

  var pool = new pg.Pool(config);

  pool.connect(function(err, client, done) {
    if(err) {
      return console.error('error fetching client from pool', err);
    }
    client.query('SELECT $1::int AS number', ['1'], function(err, result) {
      done(err);

      if(err) {
        return console.error('error running query', err);
      }
      console.log(result.rows[0].number);
      //output: 1
    });
  });

  pool.on('error', function (err, client) {
    // if an error is encountered by a client while it sits idle in the pool
    // the pool itself will emit an error event with both the error and
    // the client which emitted the original error
    // this is a rare occurrence but can happen if there is a network partition
    // between your application and the database, the database restarts, etc.
    // and so you might want to handle it and at least log it out
    console.error('idle client error', err.message, err.stack)
  });

  return pool;
};

tables.sql

create table users (
    id serial primary key,
    created_at timestamp default current_timestamp,
    name character varying(64)
);

create table companies (
    id serial primary key,
    created_at timestamp default current_timestamp,
    name character varying(64)
);

create table teams (
    id serial primary key,
    company_id integer references companies (id),
    user_id integer references users (id),
    contact_user boolean default false
);

create table listings (
    id serial primary key,
    created_at timestamp default current_timestamp,
    created_by integer references users (id),
    name character varying(64),
    description text
);

create table applications (
    id serial primary key,
    created_at timestamp default current_timestamp,
    user_id integer references users (id),
    listing_id integer references listings (id),
    cover_letter text
);

So, finally I got rejected. I would really love to know what I did wrong.


Get this bounty!!!