BLOG

standard

Example: How did I convert async code to sync code with Promise.

24.05.2015 Posted in Javascript 2 Comments
JavaScript

I’ve just finished my first refactor to convert my node.js code to be more promisey (I believe that’s the word they use these days). There’s lots of documents out there to do this. However, I thought I should contribute more to help me understand more and might get some feedback from people who’s seen it as well.

I’m trying to create a bot to report me back the performance of my site in desktop and mobile mode. So, I thought it would be easy since Google has an API for that already so I went ahead and did this.


1
2
3
4
5
6
7
8
var request = require('request');
var urlToGetTheScore = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http://www.noppanit.com&strategy=desktop&fields=ruleGroups'

request.get(urlToGetTheScore, function (error, response, body) {
  if(error) console.log(error);
 
  console.log(JSON.parse(body).ruleGroups.SPEED.score);
});

It’s pretty easy and straight forward right but now that would only return the score of desktop. I need the score of my mobile site as well. So, I added more code to be like this.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var request = require('request');

var urlToGetTheScoreDesktop = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http://www.noppanit.com&strategy=desktop&fields=ruleGroups'

var urlToGetTheScoreMobile = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http://www.noppanit.com&strategy=mobile&fields=ruleGroups'

request.get(urlToGetTheScoreDesktop, function (error, response, body) {
  if(error) console.log(error);

  console.log(JSON.parse(body).ruleGroups.SPEED.score);
});

request.get(urlToGetTheScoreMobile, function (error, response, body) {
  if(error) console.log(error);

  console.log(JSON.parse(body).ruleGroups.SPEED.score);
});

That’s great but I want to return both scores to a client so I can report the scores rather than printing them to the console. Since, request is asynchronous you cannot guarantee which score would come first. So, I thought it’s easy. I just need to call one request after the other. So, I came up with this.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var request = require('request');

var urlToGetTheScoreDesktop = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http://www.noppanit.com&strategy=desktop&fields=ruleGroups'

var urlToGetTheScoreMobile = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http://www.noppanit.com&strategy=mobile&fields=ruleGroups'

request.get(urlToGetTheScoreDesktop, function (error, response, body) {
  if(error) reject(error);

  var desktopScore = JSON.parse(body).ruleGroups.SPEED.score;

  request.get(urlToGetTheScoreMobile, function (error, response, body) {
    if(error) reject(error);

    var mobileScore = JSON.parse(body).ruleGroups.SPEED.score;

    console.log('desktop score is ' + desktopScore + ' and mobile score is ' + mobileScore);
  });
});

 

Look at how ugly it is. Now I want to make it prettier. So, I will use Promise to make it look nicer. As a good engineer I need to create a test first.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var perfModule = require('./pagespeed'),
sinon = require('sinon'),
request = require('request'),
expect = require('expect.js');

describe('Performance', function() {
var server;
  beforeEach(function(done) {
    sinon.stub(request, 'get').yields(null, null, JSON.stringify({ruleGroups : { SPEED: {score:10}} }));
    done();
  });
 
  it('should send performance stats to chat room', function(done) {
    perfModule.pagespeed(function(donotknow, msg) {
      done();
      expect(msg).to.eql('desktop speed is 10 and mobile speed is 10');
    });
  });
});

I’m using Sinon.js as the mocking framework and Mocha as the testing framework which are pretty standard.

Now I can start refactor my code. At first, I wrote some code like this, just to make it work.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var request = require('request');

var getSpeed = function(strategy) {
  var url = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http://www.noppanit.com&strategy='+ strategy + '&fields=ruleGroups'
  return new Promise(function(resolve, reject) {
    request.get(url, function (error, response, body) {
      if(error) reject(error);

      console.log(body);
      resolve(JSON.parse(body).ruleGroups.SPEED.score);
    });
  });
};

var pagespeed = function(cb) {
  getSpeed('desktop').then(function(desktopSpeed) {
    getSpeed('mobile').then(function(mobileSpeed) {
      console.log('desktop speed is ' + desktopSpeed + ' and mobile speed is ' + mobileSpeed);
    });

  });
};

exports.pagespeed = pagespeed;

Any good JavaScript developer would be like, WTH!. You still have callbacks. I thought Promise would solve that issue already! Now, I could use the power of Promise.all which takes array of promises and return array of results. My final code would look something like this.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var request = require('request'),
Promise = require('promise');

var getSpeed = function(strategy) {
  var url = 'https://www.googleapis.com/pagespeedonline/v2/runPagespeed?url=http%3A%2F%2Ffusion.net&strategy='+ strategy + '&fields=ruleGroups'
  return new Promise(function(resolve, reject) {
    request.get(url, function (error, response, body) {
      if(error) reject(error);

      console.log(body);
      resolve(JSON.parse(body).ruleGroups.SPEED.score);
    });
  });
};

var pagespeed = function(cb) {
  Promise.all([getSpeed('desktop'), getSpeed('mobile')]).then(function(speed) {
    var desktop = speed[0];
    var mobile = speed[1];

    console.log(null, 'desktop speed is ' + desktop + ' and mobile speed is ' + mobile);
  }).catch(function(error) {
    console.log(error);
  });
};

exports.pagespeed = pagespeed;

I’m not an expert in Promise and I welcome any feedback that would help improve my code.

Reference
ES6 Promises
We have a problem with promises

standard

Rise of the (Slack)Bots.

23.05.2015 Posted in Knowledges No Comments

Fusion tech team just had a hackday in the theme of “Slackbot”. We brainstormed what or how do we make the job of editorial or engineering team easier. We threw a bunch of ideas and my team decided to create a bot that can interact with you in a number of ways or encourage you to fix or debug code. We tried to make it funny and in the same time useful for our daily routine.

My colleague Daniel Bachhuber came up with the name Rubberduck which I believe he got it from here. The idea of the bot is easy. If you have used Slack you must have seen Slackbot before where it guides you how to use Slack or the bot can help you change your profile picture.

We spent a good one hour to find the best possible solution for creating a bot and we found Superscript, which has a client for Slack. It’s perfect!.

We’ve also opensourced the bot which you can clone and play around with it as well.

The first plugin we are thinking is performance bot where the bot can report the current performance of a website instead of going to a dashboard. It’s because we have remote team. So, having a bit giant board wouldn’t make much sense since some of our folks are distributed and we mainly use Slack for any communication.

rubberduck bot

Now, go ahead and create your own bot!.

 

Check this out. Rise of the bots

standard

Installing SSL on Amazon CloudFront

22.05.2015 Posted in Security, Techniques No Comments

We have just installed SSL on our Amazon CloudFront. We followed this blog post from Bryce which I think it’s really good already. However, I was stuck on the last part where you have to upload the certificate to IAM. The difference is that I had to concatenate the crt files myself and I didn’t know how to do it. So I spent quite a long time to figure it out. I thought I would write this so it might help save sometime for anybody.

If your SSL providers gave you the chained certificate already, then you don’t have to do anything else. However, when I downloaded my crt files I found this

1
2
3
4
my_domain.crt
AddTrustExternalCARoot.crt
TrustedSecureCertificateAuthority5.crt
USERTrustRSAAddTrustCA.rt

And I’m shocked. So, I thought I would need to upload all of them three times which I did but only one got through and I thought the others must have been backup or some kind (I know I’m pretty stupid). So, I used SSL Checker to check and the site said my trust is broken. I thought how could it be I did everything right.

It turned out that I didn’t upload all of the chained certificate. So, I went again and I tried to upload the second file which I got a nice error from Amazon that my certificate is malformed. I’m stumped again.

After a lot of digging and reading. I have to figured out the order of certificates and concatenate all the certificates in the right order until the root. You can ask your SSL provider if you want a quicker answer but I went the hard way.

You can run this command

1
openssl x509 -text -noout -in your_domain.crt

You should start with your domain.crt file which will be something like this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            xxxxxx
        Signature Algorithm: xxxxx
        Issuer: C=US, ST=DE, L=Wilmington, O=Corporation Service Company, CN=Trusted Secure Certificate Authority 5
        Validity
            Not Before:
            Not After :
        Subject: C=US/postalCode=x, ST=xx, L=xxx, O=xxx, LLC, OU=xxx, OU=xxx, CN=xxx
</cod>

You just need to look for <strong>Issuer</strong> which will tell you what is your next immediate certificate. In this case <strong>Trusted Secure Certificate Authority 5</strong> is my first certificate and then you go on and do the next one.

<code lang="bash">
openssl x509 -text -noout -in TrustedSecureCertificateAuthority5.crt

You will get something like this

1
2
3
4
5
6
7
8
9
10
11
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            xxxxx
        Signature Algorithm: xxxx
        Issuer: C=US, ST=New Jersey, L=Jersey City, O=The USERTRUST Network, CN=USERTrust RSA Certification Authority
        Validity
            Not Before: Sep 10 00:00:00 2014 GMT
            Not After : Sep  9 23:59:59 2024 GMT
        Subject: C=US, ST=DE, L=Wilmington, O=Corporation Service Company, CN=Trusted Secure Certificate Authority 5

It means USERTrust is the next certificate then repeat the process again until you see this.


1
2
3
4
5
6
7
8
9
10
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: xxxx
        Signature Algorithm: xxxxx
        Issuer: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root
        Validity
            Not Before: May 30 10:48:38 2000 GMT
            Not After : May 30 10:48:38 2020 GMT
        Subject: C=SE, O=AddTrust AB, OU=AddTrust External TTP Network, CN=AddTrust External CA Root

If Issuer is the same as Subject that means this certificate is the root which is going to be the last. Now, what you can do is to concatenate in the correct order of all the certificates.

You can use this command or you can use your favourite editor to do as well.

1
cat first_crt second_crt third_crt > your_pem_file

Then when you’re ready to upload the certificate to Amazon you can just do this.

1
2
3
4
5
aws iam upload-server-certificate --server-certificate-name your_domain \
--certificate-body file://your_domain.crt \
--private-key file://your_domain.private \
--certificate-chain file://your_pem_file \
--path /cloudfront/

The –certificate-chain should be your concatenated certificates.

The format of the pem should be something like this.

1
2
3
4
5
6
-----BEGIN CERTIFICATE-----
Intermediate certificate 2
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
Intermediate certificate 1
-----END CERTIFICATE-----