Category Archives: TUTORIALS

Detailed tutorials explaining how to build most common applications, in different languages.

Range Sum of BST

Task

Given the root node of a binary search tree and two integers low and high, return the sum of values of all nodes with a value in the inclusive range [low, high].

 

Example 1:

Input:

 root = [10,5,15,3,7,null,18], low = 7, high = 15

Output:

 32

Explanation:

 Nodes 7, 10, and 15 are in the range [7, 15]. 7 + 10 + 15 = 32.

Example 2:

Input:

 root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10

Output:

 23

Explanation:

 Nodes 6, 7, and 10 are in the range [6, 10]. 6 + 7 + 10 = 23.

 

Constraints:

  • The number of nodes in the tree is in the range [1, 2 * 104].
  • 1 <= Node.val <= 105
  • 1 <= low <= high <= 105
  • All Node.val are unique.

This problem was taken from https://leetcode.com/problems/range-sum-of-bst/

 

Solution

 

DFS Solution

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {number}
 */
var rangeSumBST = function(root, low, high) {
    if(!root) {
        return;
    }
    console.log(root.val);
    var queue = [root];
    var visited = {};
    var result = 0.0;    

    function dfsTraverse(root, low, high) {
    
        var childNodes = [root?.left, root?.right];

        for(const childNode of childNodes) {
          if(childNode) {
            var val = childNode.val;
            console.log(val);
            dfsTraverse(childNode, low, high);            
          }
        }

    }

    dfsTraverse(root, low, high);
    return result;
};


 function TreeNode(val, left, right) {
     this.val = (val===undefined ? 0 : val)
     this.left = (left===undefined ? null : left)
     this.right = (right===undefined ? null : right)
 }

const treeNode = new TreeNode(
    10,
    new TreeNode(
        5,
        new TreeNode(3),
        new TreeNode(7)
    ),
    new TreeNode(
        15,
        null,
        new TreeNode(18)
    )
)


rangeSumBST(treeNode, 7, 15);

 

BFS solution

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} root
 * @param {number} low
 * @param {number} high
 * @return {number}
 */
var rangeSumBST = function(root, low, high) {
    if(!root) {
        return 0;
    }
    var queue = [root];
    var visited = {};
    var result = 0.0;    
    if(root.val >= low && root.val <= high) {
        result = root.val;
    }
    
    var leftPointer = 0;
    while(leftPointer <= queue.length) {
        var curentNode = queue[leftPointer];
        leftPointer ++;
        var childNodes = [curentNode?.left, curentNode?.right];
        
        for(const childNode of childNodes) {
            if(childNode) {
                console.log(childNode.val)
                if(childNode.val >= low && childNode.val <= high) {
                    result += childNode.val;
                }
                queue.push(childNode);
            }
        }
        
           
    }
    return result;
    
};

 

Graph traversal

The graph

The graph represent connections between airports. Nodes (vertices)  represent airports, and  edges represent flights.

BFS search (Breadth First Search)

Breadth-first Search (BFS) starts by pushing all of the direct children to a queue (first-in, first-out). It then visits each item in queue and adds the next layer of children to the back of the queue. Since one node could be visited multiple times causing infinite loop, we have to keep track of all visited nodes.

Using JavaScript Map

// DATA
const airports = 'PHX BKK OKC JFK LAX MEX EZE HEL LOS LAP LIM'.split(' ');

const routes = [
    ['PHX', 'LAX'],
    ['PHX', 'JFK'],
    ['JFK', 'OKC'],
    ['JFK', 'HEL'],
    ['JFK', 'LOS'],
    ['MEX', 'LAX'],
    ['MEX', 'BKK'],
    ['MEX', 'LIM'],
    ['MEX', 'EZE'],
    ['LIM', 'BKK'],
];


// The graph
const adjacencyList = new Map();

// Add node
function addNode(airport) {
    adjacencyList.set(airport, []);
}

// Add edge, undirected
function addEdge(origin, destination) {
    adjacencyList.get(origin).push(destination);
    adjacencyList.get(destination).push(origin);
}

// Create the Graph
airports.forEach(addNode);
routes.forEach(route => addEdge(route[0], route[1]));

console.log(adjacencyList);


function bfs(start, searchItem) {

    const visited = new Set();
    const queue = [start];


    while (queue.length > 0) {

        const airport = queue.shift(); // mutates the queue
        const destinations = adjacencyList.get(airport);


        for (const destination of destinations) {
            if (destination === searchItem)  {
                console.log(`BFS found a route to`, searchItem)
            }

            if (!visited.has(destination)) {
                visited.add(destination);
                queue.push(destination);
            }           
        }        
    }

}

bfs('PHX', 'BKK');

 

Using JavaScript object

// Data
var airports = {
    'PHX': ['LAX', 'JFK'],
    'BKK': ['MEX', 'LIM'],
    'OKC': ['JFK'],
    'JFK': ['LAX', 'PHX', 'OKC', 'HEL', 'LOS', 'EZE'],
    'LAX': ['PHX', 'JFK', 'MEX'],
    'MEX': ['LAX', 'EZE', 'BKK', 'LIM'],
    'EZE': ['JFK', 'MEX'],
    'HEL': ['JFK'],
    'LOS': ['JFK'],
    'LAP': [],
    'LIM': ['MEX', 'BKK']
};


function bfs(start, endDest) {
    var queue = [start];
    var visited = {};

    while(queue.length > 0) {
        var curentNodeVal = queue.shift();
        var childNodes = airports[curentNodeVal];
        for(const childNode of childNodes) {
            if(childNode === endDest) {
                console.log("BFS found a route to :", endDest);
            }            

            if(!visited[childNode]) {
                console.log(childNode);
                visited[childNode] = true;
                queue.push( childNode);          
            }
        }
    }
}


bfs('PHX', 'BKK');

 

DFS (Depth First Search)

Depth-first Search (DFS) will go as far into the graph as possible until it reaches a node without any children, at which point it backtracks and continues the process. The algorithm can be implemented with a recursive function that keeps track of previously visited nodes. If a node has not been visited, we call the function recursively.

Using JavaScript Map

// DATA
const airports = 'PHX BKK OKC JFK LAX MEX EZE HEL LOS LAP LIM'.split(' ');

const routes = [
    ['PHX', 'LAX'],
    ['PHX', 'JFK'],
    ['JFK', 'OKC'],
    ['JFK', 'HEL'],
    ['JFK', 'LOS'],
    ['MEX', 'LAX'],
    ['MEX', 'BKK'],
    ['MEX', 'LIM'],
    ['MEX', 'EZE'],
    ['LIM', 'BKK'],
];

// The graph
const adjacencyList = new Map();

// Add node
function addNode(airport) {
    adjacencyList.set(airport, []);
}

// Add edge, undirected
function addEdge(origin, destination) {
    adjacencyList.get(origin).push(destination);
    adjacencyList.get(destination).push(origin);
}

// Create the Graph
airports.forEach(addNode);
routes.forEach(route => addEdge(route[0], route[1]));

console.log(adjacencyList);



function dfs(start, visited = new Set()) {

    console.log(start)
    
    visited.add(start);

    const destinations = adjacencyList.get(start);

    for (const destination of destinations) {

        if (destination === 'BKK') { 
            console.log(`DFS found Bangkok`)
            return;
        }
        
        if (!visited.has(destination)) {
            dfs(destination, visited);
        }

    }

}

dfs('PHX');

Using JavaScript object

// Data
var airports = {
    'PHX': ['LAX', 'JFK'],
    'BKK': ['MEX', 'LIM'],
    'OKC': ['JFK'],
    'JFK': ['LAX', 'PHX', 'OKC', 'HEL', 'LOS', 'EZE'],
    'LAX': ['PHX', 'JFK', 'MEX'],
    'MEX': ['LAX', 'EZE', 'BKK', 'LIM'],
    'EZE': ['JFK', 'MEX'],
    'HEL': ['JFK'],
    'LOS': ['JFK'],
    'LAP': [],
    'LIM': ['MEX', 'BKK']
};


function dfs(start, endDest, visited = {}) {
        
    visited[start] = true;
    console.log(start);

    const destinations = airports[start];

    for(const destination of  destinations) {
        if (destination === endDest) { 
            console.log(`DFS found route to`, endDest);
        }

        if(!visited[destination]) {
            dfs(destination, endDest, visited);
        }
        
    }
}


dfs('PHX', 'BKK');

 

Moving Average from Data Stream

Task

Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window.

Implement the MovingAverage class:

  • MovingAverage(int size) Initializes the object with the size of the window size.
  • double next(int val) Returns the moving average of the last size values of the stream.

 

Example 1:

Input

["MovingAverage", "next", "next", "next", "next"]
[[3], [1], [10], [3], [5]]

Output

[null, 1.0, 5.5, 4.66667, 6.0]

Explanation

MovingAverage movingAverage = new MovingAverage(3);
movingAverage.next(1); // return 1.0 = 1 / 1
movingAverage.next(10); // return 5.5 = (1 + 10) / 2
movingAverage.next(3); // return 4.66667 = (1 + 10 + 3) / 3
movingAverage.next(5); // return 6.0 = (10 + 3 + 5) / 3

 

Constraints:

  • 1 <= size <= 1000
  • -105 <= val <= 105
  • At most 104 calls will be made to next.

Solution

/**
 * @param {number} size
 */
var MovingAverage = function(size) {
    this.n = size;
    this.queue = [];
    this.average = 0.0;
};

/** 
 * @param {number} val
 * @return {number}
 */
MovingAverage.prototype.next = function(val) {
    var removedVal;
    if(this.queue.length >= this.n) {
        removedVal = this.queue.shift();
        this.average = this.average - removedVal;
    }

    this.queue.push(val);
    
    this.average += val;
    console.log(this.average / this.queue.length);
    return this.average / this.queue.length;
};


var movingAverage = new MovingAverage(3);
movingAverage.next(1); // return 1.0 = 1 / 1
movingAverage.next(10); // return 5.5 = (1 + 10) / 2
movingAverage.next(3); // return 4.66667 = (1 + 10 + 3) / 3
movingAverage.next(5); // return 6.0 = (10 + 3 + 5) / 3

 

Heaps or priority queues in Java Script

Still work in progress …

 

Part I Heapify …

 

heap-sorting

 

Part II sort and heapify

We start swapping values and heapify each time starting from 0 to the length of i

function heapSort(arr) {

    function maxHeapify(arr, i, N) {
        var leftChild = i * 2 + 1;
        var rightChild = i * 2 + 2;
        var largest = i;
    
        if(leftChild < N && arr[leftChild] > arr[largest]) {
            largest = leftChild;
        }
    
        if(rightChild < N && arr[rightChild] > arr[largest]) {
            largest = rightChild;
        }
    
        if(largest != i) {
            var temp = arr[i];
            arr[i] = arr[largest];
            arr[largest] = temp;
            maxHeapify(arr, largest, N);
        }
    }
    
    // CREATE A HEAP
    for(var i = parseInt(arr.length / 2 - 1); i >= 0; i--) {
        maxHeapify(arr, i, arr.length);
    }
    
    console.log("heap represented in array: ", arr);
    
    // SORT THE ARRAY
    for(var i =  arr.length - 1; i >= 0; i --) {
        var temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
    
        maxHeapify(arr, 0, i);
    }
}

const arr = [4, 6, 3, 2, 9, 1];
heapSort(arr);

console.log("sorted array:", arr);

 

Implement pow(x, n)

Task

Implement pow(x, n), which calculates x raised to the power n (i.e., xn).

 

Example 1:

Input:

 x = 2.00000, n = 10

Output:

 1024.00000

Example 2:

Input:

 x = 2.10000, n = 3

Output:

 9.26100

Example 3:

Input:

 x = 2.00000, n = -2

Output:

 0.25000

Explanation:

 2

-2

 = 1/2

2

 = 1/4 = 0.25

 

Constraints:

  • -100.0 < x < 100.0
  • -231 <= n <= 231-1
  • -104 <= xn <= 104

This problem was taken from https://leetcode.com/problems/powx-n/

 

Solution

 

Brute force solution:

Straight forward, but we have to consider negative ranges.

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    
    if(n === 0) {
        return 1;
    }

    var N = n;
    var X = x;
    var result = 1;
    
    if(n < 0) {
        X = 1 / x;
        N = -n;
    }
    
    for(var i = 0; i < N; i ++) {
        result = result * X;
    }  
    return result;
};

 

Approach 2: Fast Power Algorithm Recursive

  • divide n so you immediately cut the computation time in half to logarithmic one.

pow of 2 to the power of 6

 

 

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    
    function fastPow(x, n) {
        if(n < 1) {
            return 1;
        }
        var half = fastPow(x, Math.floor(n / 2));

        if(n % 2 == 0) {
            return half * half;
        }
        else {
            return half * half * x;
        }
    }
    
    var X = x;
    var N = n;
    if(n < 0) {
        X = 1 / x;
        N = -n;        
    }
    return fastPow(X, N);
    
};

 

 

Longest Common Prefix

Task

 

Write a function to find the longest common prefix string amongst an array of strings.

If there is no common prefix, return an empty string "".

 

Example 1:

Input:

 strs = ["flower","flow","flight"]

Output:

 "fl"

Example 2:

Input:

 strs = ["dog","racecar","car"]

Output:

 ""

Explanation:

 There is no common prefix among the input strings.

 

Constraints:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] consists of only lower-case English letters.

This problem was taken from Leetcode Longest Common Prefix

 

Solution

 

Let’s examine this example:

 strs = ["flower","flow","flight"]

the longest common substring is:

 "fl"

Solution 1: Horizontal scanning

We could assume that the whole word could be the common one so we set prefix = ‘flower’
Then we would compare with the rest words and keep removing last character until prefix becomes empty (meaning no common substring was found) or until we have the common substring.

prefix flower flow flight
flower flower flower flower
flowe flowe flowe flowe
flow flow flow flow
flo flo flo flo
fl fl fl fl

 

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    var prefix = strs[0];
    for(var i = 1; i < strs.length; i ++ ) {
        while(strs[i].indexOf(prefix) != 0) {
            prefix = prefix.substring(0, prefix.length - 1);
            if(prefix == "")
                return '';
        }
    }
    return prefix;    
};

 what we just did:
– set up prefix to be the whole 1st word strs[0]
– compare prefix with the second word (strs[1]) and if there is no match, remove the last symbol and keep going until it finds match.

Complexity Analysis

  • Time complexity : O(S) , where S is the sum of all characters in all strings.In the worst case all n strings are the same. The algorithm compares the string S1 with the other strings [S_2 \ldots S_n] There are S character comparisons, where S is the sum of all characters in the input array.
  • Space complexity : O(1). We only used constant extra space.

 

Solution 2: Vertical scanning

Similar but optimized for cases like the one above where we have very short common substring, and we don’t want to scan the whole word.

 

prefix flower flow flight
f f f f
fl fl fl fl
flo flo flo flo

 

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    var prefix;    
    for(var i = 0; i < strs[0].length; i ++ ) {
        var c = strs[0][i];
        for(var j = 0; j < strs.length; j++) {
            if(strs[j][i] != c) {
                return strs[0].substring(0, i);
            }
        }
    }
    return strs[0];    
};

 what we just did:
– Iterate through the words like they are in column.
– compare each character (starting with the first one) between all words. When we find a mismatch, remove the last (mismatched) character and return truncates strs[0]

Roman to Integer

Task

 

Roman numerals are represented by seven different symbols: IVXLCD and M.

 

Symbol                Value

I             1
V             5
X             10
L             50
C             100
D             500
M             1000

 

For example, 2 is written as II in Roman numeral, just two one’s added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II.

Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used:

  • I can be placed before V (5) and X (10) to make 4 and 9.
  • X can be placed before L (50) and C (100) to make 40 and 90.
  • C can be placed before D (500) and M (1000) to make 400 and 900.

Given a roman numeral, convert it to an integer.

 

Example 1:

Input:

 s = "III"

Output:

 3

Example 2:

Input:

 s = "IV"

Output:

 4

Example 3:

Input:

 s = "IX"

Output:

 9

Example 4:

Input:

 s = "LVIII"

Output:

 58

Explanation:

 L = 50, V= 5, III = 3.

Example 5:

Input:

 s = "MCMXCIV"

Output:

 1994

Explanation:

 M = 1000, CM = 900, XC = 90 and IV = 4.

 

Constraints:

  • 1 <= s.length <= 15
  • s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M').
  • It is guaranteed that s is a valid roman numeral in the range [1, 3999].

This problem was taken from Leetcode Roman To Integer

 

Solution

Solution 1: Left to right pass

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var len = s.length;
    var i = 0;
    var map = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    }
    var sum = 0;
    while(i < len) {
        var currentVal = map[ s[i] ];
        var nextVal = map[ s[i + 1] ];
        if( currentVal < nextVal) {
            sum += nextVal - currentVal;
            i ++;            
        }
        else {
            sum += currentVal;
        }
        i ++;
    }
    return sum;
};

Solution 2: Left to right (or right to left) pass improved

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var len = s.length;
    var i = 0;
    var map = {
        'I': 1,
        'IV': 4,
        'V': 5,
        'IX': 9, 
        'X': 10,
        'XL': 40,
        'L': 50,
        'XC': 90,
        'C': 100,
        'CD': 400,
        'D': 500,
        'CM': 900,
        'M': 1000
    }
    var sum = 0;
    while(i < len) {
        var currentVal = map[ s[i] ];
        var nextVal = map[ s[i + 1] ];
        if( currentVal < nextVal) {
            var sumbol = s[i] + s[i+1];
            sum += map[sumbol];
            i ++;            
        }
        else {
            sum += currentVal;
        }
        i ++;
    }
    return sum;
};

Solution3: Right to left pass

In the “subtraction” cases, such as XC, we’ve been updating our running sum as follows:

sum += value(C) - value(X)

However, notice that this is mathematically equivalent to the following:

sum += value(C)
sum -= value(X)

Utilizing this means that we can process one symbol each time we go around the main loop. We still need to determine whether or not our current symbol should be added or subtracted by looking at the neighbour though.

This way we could start from the most right symbol an initialize the sym with it, since every most right symbol will always be added to the sum.

 

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var len = s.length;
    var i = len - 1;
    var map = {
        'I': 1,
        'V': 5,
        'X': 10,
        'L': 50,
        'C': 100,
        'D': 500,
        'M': 1000
    }
    var sum = map[ s[i] ];
    i --;
    while(i > -1) {
        var currentVal = map[ s[i] ];
        var prevVal = map[ s[i + 1] ];
        if( currentVal < prevVal) {
            sum -= currentVal;          
        }
        else {
            sum += currentVal;
        }
        i --;
    }
    return sum;
};

Container With Most Water

Task

 

Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai)n vertical lines are drawn such that the two endpoints of the line i is at (i, ai) and (i, 0). Find two lines, which, together with the x-axis forms a container, such that the container contains the most water.

Notice that you may not slant the container.

 

Example 1:

Input:

 height = [1,8,6,2,5,4,8,3,7]

Output:

 49

Explanation:

 The above vertical lines are represented by array [1,8,6,2,5,4,8,3,7]. In this case, the max area of water (blue section) the container can contain is 49.

Example 2:

Input:

 height = [1,1]

Output:

 1

Example 3:

Input:

 height = [4,3,2,1,4]

Output:

 16

Example 4:

Input:

 height = [1,2,1]

Output:

 2

 

This problem was taken from Leetcode Container With Most Water

 

Solution

A better than brute force solution is to use a variation of “sliding doors” algorithm.

Let’s consider this case: [1,3,4,3]. The area with most water will be the one with highest height and length.

To find it we set up two pointers: one at position 0, and one at the end of the array. The amount of water that could be collected here is min(leftPointerValue, rightPointerValue) * length,
where length is rightPointer – leftPointer. Which is 4.

Now it’s clear that if rightPointerValue > leftPointerValue there is no point of keep moving rightPointer because we won’t get any bigger amount of water since it will always be limited by the leftPointerValue (height) and the length will always be smaller than the previous length.

So in this case we will move the leftPointer forward to evaluate the next case.

Here the amount of the water collected is min(leftPointerValue, rightPointerValue) * length which is min(3, 3) * 3 = 9.

Nex we continue evaluating all cases till leftPointer = rightPointer (length = 0), and we didn’t find bigger amount of water collected, so the answer we found on the second evaluation is the right answer: 9.

 

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {


  var maxArea = 0;
  var pLeft = 0;
  var pRight = height.length - 1;
  var len = pRight - pLeft;

  while(len > 0) {
    var pLeftVal = height[pLeft];
    var pRightVal = height[pRight];

    if(pLeftVal > pRightVal) {
      maxArea = Math.max( len * pRightVal, maxArea );
      pRight --;
    }
    else {
      maxArea = Math.max( len * pLeftVal, maxArea );
      pLeft ++;
    }
    len --;
  }    
  return maxArea;
};



var height = [1,8,6,2,5,4,8,3,7];
console.log( maxArea(height) );

 

Install SonarQube and add it as a verification step in Jenkins

Install SonarQube

Instructions

Install SonarQube. This is a reporting tool.

$ brew install sonar

But it also requires Sonar-scanner in order to scan the code.

$ brew install sonar-scanner

 

Install Jest Sonar reporter

Jest sonar reporter is a plug-in that wi
jest-sonar-reporter is a custom results processor for Jest. The processor converts Jest’s output into Sonar’s generic test data format.

In other words we need this in order for SonarQube to to be able to process the result of Jest tests.

$ yarn add jest-sonar-reporter --dev

Let’s configure jest to use sonar-reporter and also to process and exclude of testing certain folders:

./package.json

...
  "jest": {
    "setupFiles": [
      "./src/setupTests.js"
    ],
    "collectCoverageFrom": [
      "**/*.{js,jsx}",
      "!coverage/**",
      "!node_modules/**",
      "!src/index.js",
      "!src/setupTests.js",
      "!public/**",
      "!server-build/**"
    ],
    "moduleNameMapper": {
      "\\.(css|less|scss)$": "<rootDir>/src/__mocks__/styleMock.js"
    },
    "coverageDirectory": "reports/coverage",
    "coverageReporters": [
      "json",
      "lcov",
      "text"
    ],
    "testResultsProcessor": "jest-sonar-reporter"
  },
  "jestSonar": {
    "reportPath": "reports",
    "reportFile": "test-reporter.xml"
  },
...
what we just did:
– lines 6 -14 we described which folders to be added in the coverage (“**/*.{js,jsx}”) and which to be excluded. (All that start with exclamation mark).
– line 18 describes where to create the report file, which sonarqube scanner will use.

Add Sonar-project.properties file

This file is used to configure sonnar-scanenr.

./sonar-project.properties

sonar.projectKey="SparkJS-key"
sonar.projectName="SparkJS"
sonar.sourceEncoding=UTF-8
sonar.sources="./src"
sonar.tests="./src"
sonar.exclusions=**/*.test.js
sonar.test.inclusions=**/*.test.js
sonar.javascript.lcov.reportPaths=reports/coverage/lcov.info
sonar.testExecutionReportPaths=reports/test-reporter.xml

what we just did:
projectKey – is the unique identifier of the project.
projectName – is the name of the project.
sources – describes which folders to scan
tests – where the scanner will look for test files.
exclusions – which files should be excluded from coverage.

Create SonarQube project

Run sonar-scanner inside the root of the project:

$ sonar-scanner

Navigate to SonarQube web UI at the projects section, and you will see the new project created.

Integrating SonarQube quality tests with Jenkins

Before you continue make sure that you have Jenkins installed locally and is configured to run tests and deploy your project. Detailed instructions are found here:

Adding continuous integration with Jenkins pipeline and Github webhooks

After you have Jenkins set up and configured, we could proceed to adding another two `stages` into the pipeline:

– Running SonarQube Scanner
Quality Gate

Adding SonarQube plug-in for Jenkins

Open Jenkins Web UI ->Manage Jenkins->Manage Plugins and add ‘SonarQube Scanner for Jenkins

Configuring Jenkins pipeline to runs Sonar-scanner and do Quality gate.


Open
./jenkins/pr.groovy

 

pipeline {
  agent any
    
  tools {nodejs "SparkJS"}
    
  stages {
        
    stage('Cloning Git Repo') {
      steps {
        git 'https://github.com/ToniNichev/projects-sparkjs.git'
      }
    }
    stage('Install dependencies') {
      steps {
        echo '#################################'              
        echo 'Building...'       
        echo '#################################'                      
        sh '/usr/local/bin/yarn install'
      }
    }   
     
    stage('Running Tests') {
      steps {
        echo '#################################'              
        echo 'Running tests ...'          
        echo '#################################'               
         sh '/usr/local/bin/yarn test'
      }
    }    

    stage('Running ESLint') {
      steps {
        echo '#################################'              
        echo 'Running ESLint ...'          
        echo '#################################'               
         sh '/usr/local/bin/yarn lint'
      }
    }       

    stage('Running SonarQube Scanner') {
       steps {
        script {
          // requires SonarQube Scanner 2.8+
          scannerHome = tool 'SonarScanner'
        }
        withSonarQubeEnv('Tonis SonarQube') { // If you have configured more than one global server connection, you can specify its name
          sh "${scannerHome}/bin/sonar-scanner"
        }
       }
    }
   
    // No need to occupy a node
    stage("Quality Gate"){
     steps {
         script {
            timeout(time: 2, unit: 'MINUTES') { // Just in case something goes wrong, pipeline will be killed after a timeout
              def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
              if (qg.status != 'OK') {
                error "Pipeline aborted due to quality gate failure: ${qg.status}"
              }
            }
         }
      }
    }

    stage('Deploy and run server') {
      steps { 
        echo 'Starting server ...'
        sh '/usr/local/bin/yarn clean; /usr/local/bin/yarn build-prod; /usr/local/bin/yarn build-prod-ssr;'
        sh '/usr/local/bin/pm2 start ./server-build/server-bundle.js -f'
      }
    }      
  }
}

Now make some code changes like for example ./env.production change the app name title i.e.

APP_NAME=Webpack React Tutorial Production 2

Push your changes and navigate back to Jenkins WEB UI. Now two new steps will be added to the pipeline:”Running SonarQube Scanner”and”Quality Gate”

Working with danger JS and integrating it with GitHub

What is danger JS ?

Danger runs during your CI process, and gives teams the chance to automate common code review chores. It’s an automation checker against any opened pull requests.

Install danger JS

Navigate to the root of your folder and execute:

$ yarn add danger --dev; yarn danger init

yarn danger init
yarn run v1.22.4
$ /Users/toninichev/Cloud/workspace/nodeJS/Examples/Sparkjs/node_modules/.bin/danger init
Welcome to Danger Init - this will take you through setting up Danger for this project.
There are four main steps we need to do:

 - [ ] Create a Dangerfile and add a few simple rules.
 - [ ] Create a GitHub account for Danger to use, for messaging.
 - [ ] Set up an access token for Danger.
 - [ ] Set up Danger to run on your CI.

But before we start, we need one bit of information from you.
Is this is for an Open Source or private project?

[1] Open Source
[2] Private Repo
[0] CANCEL

1

## Step 1: Creating a starter Dangerfile


I've set up an example Dangerfile for you in this folder.

> cat /Users/toninichev/Cloud/workspace/nodeJS/Examples/Sparkjs/dangerfile.js 

  import {danger, warn} from 'danger'
  
    
  // No PR is too small to include a description of why you made a change
  if (danger.github.pr.body.length < 10) {
    warn('Please include a description of your PR changes.');
  }
  
  
  // Request changes to src also include changes to tests.
  const allFiles = danger.git.modified_files.concat(danger.git.created_files)
  const hasAppChanges = allFiles.some(p => includes(p, 'src/'))
  const hasTestChanges = allFiles.some(p => includes(p, '__tests__/'))
  
  if (hasAppChanges && !hasTestChanges) {
    warn('This PR does not include changes to tests, even though it affects app code.');
  }
  
    

There's a collection of small, simple rules in here, but Danger is about being able to easily
iterate. The power comes from you having the ability to codify fixes for some of the problems
that come up in day to day programming. It can be difficult to try and see those from day 1.

If you'd like to investigate the file, and make some changes - I'll wait here,
press return when you're ready to move on...

↵ 

## Step 2: Creating a GitHub account

In order to get the most out of Danger, I'd recommend giving it the ability to post in
the code-review comment section.

https://github.com

IMO, it's best to do this by using the private mode of your browser.
Create an account like: SparkjsBot and don't forget a cool robot avatar too.

Here are great resources for creative-commons images of robots:

 - https://www.flickr.com/search/?text=robot&license=2%2C3%2C4%2C5%2C6%2C9
 - https://www.google.com/search?q=robot&tbs=sur:fmc&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwjgy8-f95jLAhWI7hoKHV_UD00QsAQIMQ&biw=1265&bih=1359

Sidenote:  Holding cmd ( ⌘ ) and double clicking a link will open it in your browser.

SparkjsBot does not need privileged access to your repo or org. This is because Danger will only
be writing comments, and you do not need special access for that.

Cool, please press return when you have your account ready (and you've verified the email...)

↵ 

## Step 3: Configuring a GitHub Personal Access Token

Here's the link, you should open this in the private session where you just created the new GitHub account

https://github.com/settings/tokens/new

For Open Source projects, I'd recommend giving the token the smallest scope possible.
This means only providing access to public_repo in the token.
This token limits Danger's abilities to just writing comments on OSS projects. We recommend
this because the token can quite easily be extracted from the environment via pull requests.

It is important that you do not store this token in your repository, as GitHub will
automatically revoke your token when pushed.


?, please press return when you have your token set up...

↵ 

## Add to CI

You need to expose a token called DANGER_GITHUB_API_TOKEN and the value is the GitHub Personal Access Token.
Depending on the CI system, this may need to be done on the machine (in the ~/.bashprofile) or in a web UI somewhere.
We have a guide for all supported CI systems on danger.systems:
http://danger.systems/js/guides/getting_started.html#setting-up-danger-to-run-on-your-ci

## Useful info

- One of the best ways to test out new rules as you build them is via bundle exec danger pr.
- You can have Danger output a lot of info via the --verbose option.
- You can look at the following Dangerfiles to get some more ideas:

  * https://github.com/artsy/emission/blob/master/dangerfile.ts
  * https://github.com/facebook/react-native/blob/master/bots/dangerfile.js
  * https://github.com/apollographql/apollo-client/blob/master/config/dangerfile.ts
  * https://github.com/styleguidist/react-styleguidist/blob/master/dangerfile.js
  * https://github.com/storybooks/storybook/blob/master/dangerfile.js
  * https://github.com/ReactiveX/rxjs/blob/master/dangerfile.js


?

And you're good to go. Danger is a collaboration between Orta Therox, Gem 'Danger' Maslen,
and every who has sent PRs.

If you like Danger, let others know. If you want to know more, follow @orta and @DangerSystems on Twitter.
If you don't like something about Danger, help us improve the project - it's all done on volunteer time! xxx
Remember: it's nice to be nice.

✨  Done in 1701.50s.

At the end this will create danger.js file in the root of your project.

Setup Github token

Create new Github token and give it “repo” access. This should be enough to allow the Github bot that will use it to post messages on pull requests.

Make sure that you copied the token and save it safely OUTSIDE OF THE PROJECT’S FOLDER,  because you won’t be able to see the token again.
Also if you save it in the project’s folder, next time when you push the code Github will invalidate it cause this is considered a security breach.

Link to create new token: https://github.com/settings/tokens/new

Override the contents of the danger file

import { fail, warn, message, markdown, danger } from "danger"

fail("Testing failure message");
warn("Testing warning");
message("Normal message");
markdown("*Markdown* is also **supported**");

const { additions = 0, deletions = 0 } = danger.github.pr;
message(`:tada: The PR added ${additions} and removed ${deletions} lines.`);

const modifiedMD = danger.git.modified_files.join("\n");
message(`Changed Files in this PR: \n ${modifiedMD} \n`);

Create pull request and test danger JS locally

Before we could do this, we have to export environment variable DANGER_GITHUB_API_TOKEN with the value equal to the tocen that we created in the previous chapter.

$ export DANGER_GITHUB_API_TOKEN="XXXXXXXXXXXXXXXXXXXXXXXX"

And now, test it locally:

$ yarn danger pr https://github.com/ToniNichev/projects-sparkjs/pull/1 

Integrate it with GitHub

Github provides api that we could use to get information about commits and pull requests.

Example of getting all pull requests:

https://api.github.com/repos/ToniNichev/projects-sparkjs/pulls?state=all

Before we are able to see messages in github we have to txport two more environment variables:

$ export DANGER_FAKE_CI="YEP"
$ export DANGER_TEST_REPO='ToniNichev/projects-sparkjs'

Test danger JS. Execute:

$ DANGER_TEST_PR='4' yarn danger ci 

And if everything is correct you should see the messages in the pull request: