Node-Elastical: Implement stats function

Recently, I had to find the size of an index in Elasticsearch. I could have just built my query using request like this:

var request = require('request');
request.get('http://localhost:9200/' + myIndex + '/_stats'
  , function (err, res, body) {
    console.log(res._all.primaries.store.size_in_bytes);
});

Fast and easy. Since it’s not available yet in node-elastical, I found it better to add this functionality so that other people can use it. Implementing the stats function wasn’t difficult. It just require parsing options to create the right url and then making the request (with request) to Elasticsearch. I used the Indices Stats API documentation to create the function.

In file client.js

stats: function (options, callback) {
  var query = [],
  url = '',
  hasOptions;

  if (typeof options === 'function') {
    callback = options;
    options = {};
  }
  //Create a copy of options so we can modify it.
  options = util.merge(options || {});

  if (options.index) {
    url = '/' + encode(Array.isArray(options.index) ?
      options.index.join(',') : options.index);
    delete options.index;
    //Look for types only if there is an index
    if (options.types) {
      query.push(encode('types') + '=' + encode(
        Array.isArray(options.types) ?
        options.types.join(',') : options.types));
    }
    delete options.types;
  }

  url += '/_stats';

  util.each(options, function (value, name) {
    if (value === true || value === false) {
      value = value ? '1' : '0';
    }

    query.push(encode(name) + '=' + encode(value));
  });

  if (query.length) {
    url += '?' + query.join('&');
  }

  this._request(url, {
    method: 'GET'
  }, function (err, res) {
    if(err) { return callback(err, null, res), undefined; }
    callback(null, res);
  });
},
In file index.js
stats: function (options, callback) {
  if (typeof options === 'function') {
    callback = options;
    options = {};
  }
  this.client.stats(util.merge(options,{index: this.name}), callback);
},

GNU/Linux: Parcours

Une minorité de personne a la chance d’entrer dans le monde de l’informatique accompagné d’un système GNU/Linux. Comme la très grande majorité, j’ai donc découvert l’informatique en commençant par un système Windows, 98 en l’occurrence. Quelques années plus tard, je reçois mon premier ordinateur perso, un portable sous Vista. Après quelques mois d’utilisation, l’envie de passer à un système GNU/Linux se fait sentir. Arrive donc une période de lecture de tuto, doc, wiki et autres forums pour savoir comment procéder et surtout, quelle distribution choisir?

Mon choix se porte vers Ubuntu, alors en version 09.04. L’installation se déroule sans aucun problème, j’ai maintenant un dual boot Ubuntu/Vista. Les mois passent et mon OS GNU/Linux devient peu à peu la référence. Je délaisse Vista, sauf pour jouer à un jeu de temps à autre.

Les mois passent, et arrive les Rencontres Mondiales du Logiciel Libre 2011 à Strasbourg. Au programme, conférence de Richard Stallman, Benjamin Bayart, Jérémie Zimmerman,… un évènement à ne pas manquer! Quelques semaines avant, j’effectue une mise à niveau vers la dernière version d’Ubuntu en date, la 11.04, et rencontre mon premier problème. Une fois la phase de login passée, seul le fond d’écran s’affiche. Pas moyen de faire apparaitre quoi que ce soit, l’interface graphique restant muette devant les clics souris. La version précédente du noyau est toujours présente dans mon Grub, je teste à tout hasard; tout y fonctionne normalement. je me rabat donc temporairement sur celle-ci en attendant de trouver une solution, que je ne trouverai finalement pas.

Dans le même temps, Stallman nous parle de liberté, pour l’utilisateur, pour le développeur. Il commence par définir le logiciel libre en trois mots: « Liberté, Égalité, Fraternité », sous un tonnerre d’applaudissements et nous expose les 4 libertés qui lui sont liés. Son discours s’appuie sur de nombreux exemples tirés de l’actualité du moment. Au passage, Stallman cite quelques distributions totalement libres, parmi celles-ci: Trisquel.

N’arrivant pas à résoudre mon problème d’affichage, je décide donc d’essayer Trisquel. Je ne suis pas dépayser, pas de changement majeur par rapport à Ubuntu. Néanmoins, les drivers wifi n’étant pas libres, ceux-ci ne sont pas présent de base. J’utilise donc Trisquel pendant un peu plus d’un trimestre, avant de prendre la décision de tester une autre distribution.

Je me tourne alors vers Debian pour un retour au source, Ubuntu et Trisquel étant construite sur une base Debian. Pas de wifi non plus après la première installation, mais quelques lignes de commande règle le problème. Je retrouve pour un temps un environnement qui me convient. Mais, car il y a un mais, la version que j’utilise est la version stable… Les logiciels ne sont donc pas disponibles dans leur dernière version. Alors que faire? Une solution consiste à passer en version testing, celle-ci étant à priori relativement stable?

Finalement, nouveau changement puisque j’utilise à présent un système Arch Linux. L’installation s’effectue entièrement en ligne de commande, en choisissant les briques qui composeront l’OS. Quelques problèmes surviennent lors de la mise en place de l’environnement de bureau, mais la lecture du wiki permet de débloquer la situation. Place ensuite à l’étape de configuration/personnalisation pour avoir un système qui me convienne parfaitement.

archlinux-logo

Après maintenant trois semaines d’utilisation quotidienne, je pense avoir trouvé l’OS qui me conviendra pour les mois (et les années?!) à venir. Agréablement surpris par les différents paquets à disposition, notamment le paquet nodejs qui permet d’éviter la compilation à partir des sources à coup de make à chaque nouvelle version. Le wiki est complet, du moins, pour les parties que j’ai pu parcourir. Satisfait donc, de cet ArchLinux tout neuf, au point que j’envisage de l’installer sur un autre ordinateur resté quant à lui sur Ubuntu.

Request: noproxy configuration

Like with npm and for the same reasons, it would be a great idea to have a noproxy configuration in request.
So here is the pull request!

if(self.noproxy) {
  if(typeof self.noproxy == 'string') {
    if(self.noproxy.search(self.uri.hostname) !== -1) {
      delete self.proxy
    }
  }
}

Really simple. If the hostname is in the noproxy string, we delete the proxy parameter so that it won’t be used.

And the test which validate the modification:

/*
** Test noproxy configuration.
**
** We create a server and a proxy.
** Server listens on localhost:80.
** Proxy listens on localhost:8080.
** The proxy redirects all requests to /proxy on the server.
** On the server, /proxy sends "proxy" .
** When server is directly requested, it answers with "noproxy" .
**
**
** So we perform 2 tests, both with proxy equal to "http://localhost:8080".
** -A test is performed with noproxy equal to "null". In this case,
** the server responds with "proxy" because the proxy is used.
** -In the other test, noproxy equal "localhost, example.com".
** Since localhost is part of noproxy, request is made directly
** to the server and proxy is ignored.
*/

var assert = require("assert")
  , http = require('http')
  , request = require('../main.js')
  //We create a server and a proxy.
  , server = http.createServer(function(req, res){
      res.statusCode = 200
      if(req.url == '/proxy') {
        res.end('proxy')
      } else {
        res.end('noproxy')
      }
    })
  , proxy = http.createServer(function (req, res) {
      res.statusCode = 200
      var url = 'http://localhost:80/proxy'
      var x = request(url)
      req.pipe(x)
      x.pipe(res)
    })
    ;

//Launch server and proxy
var initialize = function (cb) {
  server.listen(80, 'localhost', function () {
    proxy.listen(8080, 'localhost', cb)
  })
}

//Tests
initialize(function () {
  //Checking the route for server and proxy
  request.get("http://localhost:80/test", function (err, res, body) {
    assert.equal(res.statusCode, 200)
    request.get("http://localhost:80/proxy", function (err, res2, body) {
      assert.equal(res2.statusCode, 200)
      request.get("http://localhost:8080/test", function (err, res3, body) {
        assert.equal(res3.statusCode, 200)
        makeNoProxyTest(function () {
          makeProxyTest(function () {
            closeServer(server)
            closeServer(proxy)
          })
        })
      })
    })
  })
})

//Request with noproxy
var makeNoProxyTest = function (cb) {
  request ({
    url: 'http://localhost:80/test',
    proxy: 'http://localhost:8080',
    noproxy: 'localhost, example.com'
  }, function (err, res, body) {
    assert.equal(body, 'noproxy')
    cb()
  })
}

//Request with proxy
var makeProxyTest = function (cb) {
  request ({
    url: 'http://localhost:80/test',
    proxy: 'http://localhost:8080',
    noproxy: 'null'
  }, function (err, res, body) {
    assert.equal(body, 'proxy')
    cb()
  })
}

var closeServer = function (s) {
  s.close()
}

Placer le focus à la fin d’un champ / Place focus at the end of a field

function setFocusBack (element) {
  var tmp = element.val();
  element.focus().val('');
  element.val(tmp);
}

element est une sélection JQuery comme par exemple: $(‘.text’).
Cette fonction permet donc de remettre facilement le focus à la fin d’un champ d’input (par exemple).

Where element is a JQuery selector like for instance $(‘.text’).
This function allows us to easily set the focus back at the end of an input field (for instance).

Html:

<input type="text" placeholder="ER" class="text"/>

File upload with Ajax and Cross-domain

I’ve recently tried to upload a file from a client to a server and faced the problem of cross-domain. Indeed, uploading is done with ajax, to be able to analyze the response. So, I had to solve 2 problems: uploading with ajax and cross-domain.

Concerning cross-domain, I’ve found many different solutions but none of them really fit my need since I needed to add informations in the header. (I think there were other reasons, but cannot remember). I find it easier to make a two step upload. First, I upload the file to my simple PushState server, then the server upload the file to the other website. This server is built using NodeJs, express and request. Upload is made with request and its form parameter which allows us to do multipart/form-data.
Here is the server:

var express = require('express');
var request = require('request');
var fs = require('fs');
var app = express();

app.use(express.logger());
app.use(express.bodyParser({uploadDir:'./tmp'}));
app.use(app.router);
app.use(express.static('./public'));
app.use(function(req, res) {
  fs.createReadStream('./public/index.html').pipe(res);
});

app.post('/upload', function(req, res, next) {
  //Configure the request
  var opts = {
    url: "http://your/url",
    method: "POST",
    headers: {
      login: "yourLogin",
      password: "yourPassword"
    }
  };

  var x = request(opts);
  var form = x.form();

  //Optionnal: Add a name and a type to form
  var fileName = req.files.file.name.split('.');
  form.append('name', fileName[0]);
  form.append('type', fileName[fileName.length - 1]);

  //Add file to the request
  form.append('file', fs.createReadStream(req.files.file.path));

  //Pipe response from website to the client
  x.pipe(res);
});

app.listen(4242, function() {
  console.log('Server running!');
});

Uploading with ajax is done thank to jquery.iframe-transport (just add it in your code).
Concerning ajax, code is really simple:

$.ajax({
  url: "http://localhost:4242/upload",
  type: 'POST',
  files: $('.fileUpload'),
  iframe: true,
  success: function(data, textStatus, jqXHR) {
    //Do whatever you want with the response
  },
  error: function(err) {
    console.error(err);
    console.log("Error while uploading the document. :/");
  }
});

The html behind:

<form enctype="multipart/form-data">
  <input class='fileUpload' type="file" name="file"/>
</form>

In this case, I listen for the event change input.fileUpload which indicates that a file has been chosen and then, call a function executing the ajax part. It would also be possible to react on click on a button.

Other solutions concerning cross-domain:                                                      http://stackoverflow.com/questions/3076414/ways-to-circumvent-the-same-origin-policy                                                                                  https://github.com/blueimp/jQuery-File-Upload/wiki/Cross-domain-uploads