<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Wojtek Krzywiec</title>
    <description>The latest articles on DEV Community by Wojtek Krzywiec (@wkrzywiec).</description>
    <link>https://dev.to/wkrzywiec</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F515722%2F9a8dd859-f16e-473d-af78-598f0a809371.jpeg</url>
      <title>DEV Community: Wojtek Krzywiec</title>
      <link>https://dev.to/wkrzywiec</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wkrzywiec"/>
    <language>en</language>
    <item>
      <title>Java Series: Flatmap</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Sun, 01 Jan 2023 17:38:44 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/java-series-flatmap-2fif</link>
      <guid>https://dev.to/wkrzywiec/java-series-flatmap-2fif</guid>
      <description>&lt;p&gt;&lt;em&gt;Java 8 was a great step forward toward modern programing language. One of the key features added in this release was Java streams. It provides lots of convenient operations for data processing. One of them is a &lt;code&gt;flatMap()&lt;/code&gt; used very widely to unwrap and merge multiple collections into one.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is part of the "Java Series", which covers useful Java functions from standard and popular libraries. More posts on that can be found here on Dev.to or on my &lt;a href="https://wkrzywiec.is-a.dev/tags/java-series?utm_source=devto" rel="noopener noreferrer"&gt;home page&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Problem statement
&lt;/h2&gt;

&lt;p&gt;Many times when we work with Java code we end up with a following Plain Old Java Object (POJO):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Parent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;childs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;They can represent database entities or data transfer objects (DTOs). In general, they're used to structure data. Let's say that we got the list of &lt;code&gt;Parent&lt;/code&gt; objects, but we want to operate on a list of all &lt;code&gt;Child&lt;/code&gt; that are part of the &lt;code&gt;Parent&lt;/code&gt;. How we could extract &lt;code&gt;Child&lt;/code&gt; objects from all &lt;code&gt;Parent&lt;/code&gt; objects and combine them into a single list? The naive approach would be to use a loop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Parent&lt;/span&gt; &lt;span class="nl"&gt;parent:&lt;/span&gt; &lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;bs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;childs&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it doesn't look nice and clean. Instead, we could use the Java stream:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Child&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="n"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;parent&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Childs&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;children&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;list&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it has a drawback too. Let's say that once we get a list of all &lt;code&gt;Child&lt;/code&gt; objects we would like to modify them, aggregate, or do calculations on them. Ideally it would be good to perform those action in the same stream. Unfortunately, this is not the case, the &lt;code&gt;forEach()&lt;/code&gt; method in the above example is ending the stream processing, which makes it impossible to process &lt;code&gt;Child&lt;/code&gt; records in the same stream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Luckily Java creators foresaw this problem and introduced a &lt;code&gt;flatMap()&lt;/code&gt; function that is part of a &lt;code&gt;java.util.stream.Stream&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;The idea is pretty straightforward. It does two things with every element of a stream:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;maps - transforms one element from a stream into a new stream, so as a result we would get a stream of streams,&lt;/li&gt;
&lt;li&gt;flattens - results of a previous operation are merged into one stream.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To visualize it consider the following situation:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8batbmpxf0zypvk54od.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8batbmpxf0zypvk54od.png" alt="Map function" width="713" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's say that we have a stream of &lt;code&gt;Author&lt;/code&gt; objects, that has a method called &lt;code&gt;books()&lt;/code&gt; which returns a list of &lt;code&gt;Book&lt;/code&gt; objects. And now let's say that we would like to have access to all books written by all authors to make further operations on them. If we would use the &lt;code&gt;map()&lt;/code&gt; function within which we would call the &lt;code&gt;books()&lt;/code&gt; method we would get a stream of lists of &lt;code&gt;Book&lt;/code&gt; objects. This is not what we want to have. &lt;/p&gt;

&lt;p&gt;What we would like to have is a stream of &lt;code&gt;Book&lt;/code&gt; objects, not a stream of their lists. How we can overcome it? Using &lt;code&gt;flatmap()&lt;/code&gt; instead:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g3aizx2bnjnlbz0d7ad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2g3aizx2bnjnlbz0d7ad.png" alt="Flatmap function" width="645" height="772"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As previously we need to invoke the &lt;code&gt;books()&lt;/code&gt; method of the &lt;code&gt;Author&lt;/code&gt; class to get a list of &lt;code&gt;Books&lt;/code&gt;. The only difference is that input needs to be converted into multiple values represented by a Java stream. This is the requirement of the &lt;code&gt;flatMap()&lt;/code&gt; method. Whatever operations we do within it needs to return a &lt;code&gt;Stream&amp;lt;T&amp;gt;&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;The same situation can be reflected with a code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;listsOfListOfBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Author:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;listOfBooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;books&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To translate a list of &lt;code&gt;Book&lt;/code&gt; objects the standard &lt;code&gt;Collection.stream()&lt;/code&gt; method was used.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use it?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Unwrap &amp;amp; operate
&lt;/h3&gt;

&lt;p&gt;The most common case when the &lt;code&gt;flatMap()&lt;/code&gt; method might be handy is when one of the stream operations produces a collection of objects and we would like to make further actions on each one of them. &lt;/p&gt;

&lt;p&gt;To visualize it let's go back to the previous example with &lt;code&gt;Book&lt;/code&gt; and &lt;code&gt;Author&lt;/code&gt; records. Here are their definitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now let's say that we want to create a method that takes a list of &lt;code&gt;Author&lt;/code&gt; objects as input and produces the list of all book titles that these authors wrote:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAllBookTitles&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;author&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;author&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;books&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Book:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After converting a list of &lt;code&gt;Author&lt;/code&gt; objects into a stream the &lt;code&gt;flatMap()&lt;/code&gt; operation is used first. The &lt;code&gt;books()&lt;/code&gt; method is invoked to get their list and then it's changed into the stream. The &lt;code&gt;flatMap()&lt;/code&gt; is then merging all resulting streams into one so next the &lt;code&gt;title()&lt;/code&gt; can be called to get a String representation of a book title. Finally, the results of each element in a stream are collected into the list.&lt;/p&gt;

&lt;p&gt;The above method can be written even better. We can split invoking &lt;code&gt;books()&lt;/code&gt; and &lt;code&gt;stream()&lt;/code&gt; methods into two operations - &lt;code&gt;map()&lt;/code&gt; and &lt;code&gt;flatMap()&lt;/code&gt; respectively - to get a nice looking code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAllBookTitles&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Author&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;authors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Author:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;books&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;List:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Book:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Merge lists
&lt;/h3&gt;

&lt;p&gt;Another case when &lt;code&gt;flatMap()&lt;/code&gt; can be very useful is when we would like to combine two or more lists (or any other &lt;code&gt;java.util.Collection&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;mergeLists&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;List:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A big plus for this approach is that after &lt;code&gt;flatMap()&lt;/code&gt; we don't need to close the stream immediately. Instead, we can apply other operations on every object, like filtering, mapping, aggregating, etc. Which is cleaner and more efficient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Get value from nested Optional
&lt;/h3&gt;

&lt;p&gt;Apart from Java streams &lt;code&gt;flatMap()&lt;/code&gt; method can be invoked on an &lt;code&gt;Optional&lt;/code&gt; object. It's used to unwrap an &lt;code&gt;Optional&lt;/code&gt; that is nested inside another &lt;code&gt;Optional&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Let's say that we've got the following record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;buildingNo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;apartmentNo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now suppose we would have an &lt;code&gt;Optional&amp;lt;Address&amp;gt;&lt;/code&gt; and would like to extract a value of an &lt;code&gt;apartmentNo&lt;/code&gt;. The code without &lt;code&gt;flatMap()&lt;/code&gt; would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;extractApartmentNo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;apartmentNo&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first step is to unwrap the value from the &lt;code&gt;Optional&lt;/code&gt;, which might be empty. Only after checking it, we can proceed with unwrapping (and handling empty values) an apartment address. &lt;/p&gt;

&lt;p&gt;This approach is ok, but can be done better with &lt;code&gt;flatMap()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;extractApartmentNo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Address&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Address:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;apartmentNo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach a way nicer than the previous one. Both Optionals - parent and child - are validated whether they hold a &lt;code&gt;null&lt;/code&gt; value in a single expression. &lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Introducing streams into Java made Data processing easier. It brings us a lot of handy operations. &lt;code&gt;flatMap()&lt;/code&gt; is one of them which gives us the possibility to merge multiple streams into one or flatten nested streams into one. It's a very common pattern and is used many times in real projects.&lt;/p&gt;

&lt;p&gt;Code and tests from this post can be found in my repository: &lt;a href="https://github.com/wkrzywiec/java-series" rel="noopener noreferrer"&gt;wkrzywiec/java-series | Github&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/java/technologies/architect-streams-pt2.html" rel="noopener noreferrer"&gt;Part 2: Processing Data with Java SE 8 Streams | Oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#flatMap-java.util.function.Function-" rel="noopener noreferrer"&gt;Interface Stream | Docs Oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/technical-resources/articles/java/java8-optional.html" rel="noopener noreferrer"&gt;Tired of Null Pointer Exceptions? Consider Using Java SE 8's "Optional"! | Oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Originally published at &lt;a href="https://wkrzywiec.is-a.dev/posts/049_java-series-flatmap?utm_source=devto" rel="noopener noreferrer"&gt;https://wkrzywiec.is-a.dev&lt;/a&gt; on December 12, 2021.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>motivation</category>
      <category>learning</category>
    </item>
    <item>
      <title>Food Delivery App</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Sun, 28 Aug 2022 18:42:13 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/food-delivery-app-1g2b</link>
      <guid>https://dev.to/wkrzywiec/food-delivery-app-1g2b</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;This is a very simple food delivery app. It allows to search for food and then order and delivery it to your home.&lt;/p&gt;

&lt;p&gt;The idea behind this was to build an event-driven distributed system. It contains 5 microservices which communicates with each other via events (most of the time). &lt;/p&gt;

&lt;p&gt;Apart from that I wanted to try to implement a simple event &lt;a href="https://microservices.io/patterns/data/event-sourcing.html" rel="noopener noreferrer"&gt;sourcing system&lt;/a&gt; in which all business events are stored as a sequence of state-changing events.&lt;/p&gt;

&lt;p&gt;In this project all these concepts are covered with Redis. &lt;/p&gt;

&lt;p&gt;But this is not everything! It also uses other Redis modules, e.g. RedisJSON  and Redis Search to show how easy it's to build a reliable system with Redis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Submission Category: Microservice Mavens
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Video Explainer of this project
&lt;/h3&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/j2OgyTJFc14"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Language Used
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Java/Spring&lt;/li&gt;
&lt;li&gt;JS/React&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec" rel="noopener noreferrer"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/food-delivery-redis" rel="noopener noreferrer"&gt;
        food-delivery-redis
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Food Delivery app&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;This is a very simple food delivery distributed system. It allows to search for food and then order them with a delivery.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/wkrzywiec/food-delivery-redis/docs/image-1.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fwkrzywiec%2Ffood-delivery-redis%2Fdocs%2Fimage-1.png" alt="image-1"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/wkrzywiec/food-delivery-redis/docs/image-2.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fwkrzywiec%2Ffood-delivery-redis%2Fdocs%2Fimage-2.png" alt="image-2"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/wkrzywiec/food-delivery-redis/docs/image-3.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fwkrzywiec%2Ffood-delivery-redis%2Fdocs%2Fimage-3.png" alt="image-3"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/wkrzywiec/food-delivery-redis/docs/image-4.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fwkrzywiec%2Ffood-delivery-redis%2Fdocs%2Fimage-4.png" alt="image-4"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The entire system is made of 5 microservices (1 React, 4 Java/Spring):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;ui&lt;/em&gt; - React application, used by customers to place orders, manage them and track deliveries, url: &lt;a href="http://localhost:80" rel="nofollow noopener noreferrer"&gt;http://localhost:80&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;bff&lt;/em&gt; - backend for frontend service, used to provide REST endpoint for &lt;em&gt;ui&lt;/em&gt;, url: &lt;a href="http://localhost:8081/swagger-ui.html" rel="nofollow noopener noreferrer"&gt;http://localhost:8081/swagger-ui.html&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;food&lt;/em&gt; - service that handles adding available meals to Redis, url: &lt;a href="http://localhost:8084/swagger-ui.html" rel="nofollow noopener noreferrer"&gt;http://localhost:8084/swagger-ui.html&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;ordering&lt;/em&gt; - core service for managing orders,&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;delivery&lt;/em&gt; - core service for managing deliveries.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Overview video (Optional)&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Here's a short video that explains the project and how it uses Redis:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://youtu.be/j2OgyTJFc14" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ce588c8b350bed64165f267072a7e198405d67f40b8bff9e7f06a52b9f97c9b4/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f6a324f6779544a466331342f64656661756c742e6a7067" alt="Watch the video"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Here is the overview of a system architecture with used Redis modules:&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/wkrzywiec/food-delivery-redis/docs/architecture.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fwkrzywiec%2Ffood-delivery-redis%2Fdocs%2Farchitecture.png" alt="architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Most of the communication is based on commands and events. E.g. in order to place an order a proper command…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/wkrzywiec/food-delivery-redis" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgu0pngquyjdj0g0n87h9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgu0pngquyjdj0g0n87h9.png" alt="architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hvi5j29mdp6rfds0sfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3hvi5j29mdp6rfds0sfr.png" alt="food search"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ips14rx215mzu23ar1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ips14rx215mzu23ar1o.png" alt="order"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hzgx98wazuk9n98wfy2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hzgx98wazuk9n98wfy2.png" alt="active-orders"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frclkf465kcveugro43mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frclkf465kcveugro43mg.png" alt="completed orders"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Check out &lt;a href="https://redis.io/docs/stack/get-started/clients/#high-level-client-libraries" rel="noopener noreferrer"&gt;Redis OM&lt;/a&gt;, client libraries for working with Redis as a multi-model database.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use &lt;a href="https://redis.info/redisinsight" rel="noopener noreferrer"&gt;RedisInsight&lt;/a&gt; to visualize your data in Redis.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sign up for a &lt;a href="https://redis.info/try-free-dev-to" rel="noopener noreferrer"&gt;free Redis database&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redishackathon</category>
      <category>eventdriven</category>
      <category>java</category>
      <category>react</category>
    </item>
    <item>
      <title>Classic Den - Azure Trial Hackathon</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Mon, 07 Mar 2022 14:15:40 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/classic-den-azure-trial-hackathon-3g22</link>
      <guid>https://dev.to/wkrzywiec/classic-den-azure-trial-hackathon-3g22</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;Classic Den is a simple website that can be used to post any announcement, thought, idea or question. Its style is inspired by the look and feel of classic PC systems.&lt;/p&gt;

&lt;p&gt;Link to a website: &lt;a href="https://polite-dune-017443c03.1.azurestaticapps.net"&gt;https://polite-dune-017443c03.1.azurestaticapps.net&lt;/a&gt;&lt;br&gt;
Link to a repository: &lt;a href="https://github.com/wkrzywiec/classic-den"&gt;https://github.com/wkrzywiec/classic-den&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My target was to create a low-cost website which could be used by small communities to post announcements or other information. The main assumption is such a page would have only a few write operations (adding posts), but lots of reads (viewing page with posts).&lt;/p&gt;

&lt;p&gt;As a result Classic Den is made of three components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;frontend - a simple, framework-less application, written with plain HTML, CSS, and JS. It's served from Azure Static Web App service.&lt;/li&gt;
&lt;li&gt;backend functions - two Java functions deployed on Azure Functions responsible for adding and removing old posts (after 14 days).&lt;/li&gt;
&lt;li&gt;GitHub repository - acts as a source of code for both frontend and backend, but also it's used to store posts. Whenever a new post is created a Java function is committing a change to this repo which triggers a GitHub Action which updates static web app with a new&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n6b3eJPR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/classic-den-diagram.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n6b3eJPR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/classic-den-diagram.png" alt="diagram" width="880" height="375"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Computing Captains&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Link to Code on GitHub
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/classic-den"&gt;
        classic-den
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Simple client and server app for publishing posts
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Classic Den&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;Project submitted to the &lt;a href="https://dev.to/devteam/hack-the-microsoft-azure-trial-on-dev-2ne5" rel="nofollow"&gt;Microsoft Azure Trial Hackathon&lt;/a&gt;. Submission post on the Dev.to.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/771cc18a712bf9edb0925a86164c34b0d803c4d9177dd4467eff7b777109c723/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a6176612d4544384230303f7374796c653d666f722d7468652d6261646765266c6f676f3d6a617661266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/771cc18a712bf9edb0925a86164c34b0d803c4d9177dd4467eff7b777109c723/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a6176612d4544384230303f7374796c653d666f722d7468652d6261646765266c6f676f3d6a617661266c6f676f436f6c6f723d7768697465" alt="java"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/60155f4543422e46101b7edb0fc701c872d9190b23dc33cb47bd1ac15d80dec1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48544d4c2d3233393132303f7374796c653d666f722d7468652d6261646765266c6f676f3d68746d6c35266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/60155f4543422e46101b7edb0fc701c872d9190b23dc33cb47bd1ac15d80dec1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48544d4c2d3233393132303f7374796c653d666f722d7468652d6261646765266c6f676f3d68746d6c35266c6f676f436f6c6f723d7768697465" alt="html"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e8ba07fa7cc79831afca90c574b74f1eefd0bf76af4e498cb0674330a1634e2a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4353532d3233393132303f267374796c653d666f722d7468652d6261646765266c6f676f3d63737333266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/e8ba07fa7cc79831afca90c574b74f1eefd0bf76af4e498cb0674330a1634e2a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4353532d3233393132303f267374796c653d666f722d7468652d6261646765266c6f676f3d63737333266c6f676f436f6c6f723d7768697465" alt="css"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9d07c04bdd98c662d5df9d4e1cc1de8446ffeaebca330feb161f1fb8e1188204/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a6176615363726970742d4637444631453f7374796c653d666f722d7468652d6261646765266c6f676f3d6a617661736372697074266c6f676f436f6c6f723d626c61636b"&gt;&lt;img src="https://camo.githubusercontent.com/9d07c04bdd98c662d5df9d4e1cc1de8446ffeaebca330feb161f1fb8e1188204/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a6176615363726970742d4637444631453f7374796c653d666f722d7468652d6261646765266c6f676f3d6a617661736372697074266c6f676f436f6c6f723d626c61636b" alt="js"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/1b617fef81925ffc4c268ca8638582254bab5b3599568a603af8137d8b7d8835/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6963726f736f66745f417a7572652d3030383944363f7374796c653d666f722d7468652d6261646765266c6f676f3d6d6963726f736f66742d617a757265266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/1b617fef81925ffc4c268ca8638582254bab5b3599568a603af8137d8b7d8835/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6963726f736f66745f417a7572652d3030383944363f7374796c653d666f722d7468652d6261646765266c6f676f3d6d6963726f736f66742d617a757265266c6f676f436f6c6f723d7768697465" alt="azure"&gt;&lt;/a&gt; &lt;a href="https://github.com/wkrzywiec/classic-den/actions/workflows/azure-static-web-apps.yml"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cjhRqN5J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/actions/workflows/azure-static-web-apps.yml/badge.svg" alt="Azure Static Web Apps CI/CD"&gt;&lt;/a&gt; &lt;a href="https://github.com/wkrzywiec/classic-den/actions/workflows/function-feature-branch.yaml"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Oo1-tx4O--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/actions/workflows/function-feature-branch.yaml/badge.svg" alt="Functions - main branch"&gt;&lt;/a&gt; &lt;a href="https://opensource.org/licenses/MIT" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/78f47a09877ba9d28da1887a93e5c3bc2efb309c1e910eb21135becd2998238a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/wkrzywiec/classic-den/assets/classic-den-demo.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gaMQYsju--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://github.com/wkrzywiec/classic-den/assets/classic-den-demo.gif" alt="classic-den-demo"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a href="https://polite-dune-017443c03.1.azurestaticapps.net" rel="nofollow"&gt;Classic Den&lt;/a&gt;&lt;/strong&gt; is a simple website that can be used to post any announcement, thought, idea or question. Its style is inspired by the look and feel of classic PC systems.&lt;/p&gt;
&lt;h2&gt;
About&lt;/h2&gt;
&lt;p&gt;This project was created for  &lt;a href="https://dev.to/devteam/hack-the-microsoft-azure-trial-on-dev-2ne5" rel="nofollow"&gt;Microsoft Azure Trial Hackathon&lt;/a&gt;. My main goal was to learn something about the Microsoft Azure services, like Azure Functions or Azure Static Web Apps.&lt;/p&gt;
&lt;p&gt;My target was to create &lt;strong&gt;a low-cost website&lt;/strong&gt; which could be used by small communities to post announcements or other information. The main assumption is such a page would have only a few write operations (adding posts), but lots of reads (viewing page with posts).&lt;/p&gt;
&lt;p&gt;As a result &lt;em&gt;Classic Den&lt;/em&gt; is made of three components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;frontend - a simple, framework-less application, written with plain HTML, CSS, and JS. It's served from &lt;a href="https://azure.microsoft.com/en-us/services/app-service/static/" rel="nofollow"&gt;Azure&lt;/a&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/wkrzywiec/classic-den"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Components
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Frontend (Azure Static Web App)
&lt;/h5&gt;

&lt;p&gt;The frontend part is built with vanilla HTML, CSS, and JS. No framework was used. &lt;/p&gt;

&lt;p&gt;There are only two external dependencies used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/kristopolous/BOOTSTRA.386"&gt;BOOTSTRA.386 project&lt;/a&gt; -  it is a JS and CSS theme inspired by the 1980s DOS. To learn more about it check its official GitHub project page or &lt;a href="https://kristopolous.github.io/BOOTSTRA.386/"&gt;the documentation with a full demo&lt;/a&gt;. If you, like me, are amazed at how it looks please support its creator.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jquery.com"&gt;jQuery&lt;/a&gt; - a very popular library, in this project used to make calls to the Azure Function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Backend (Azure Functions)
&lt;/h5&gt;

&lt;p&gt;Simple plain Java application with two functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create new post - an HTTP triggered function used to add a new entry by translating the JSON request into HTML post representation and committing it to this repository (&lt;code&gt;entries.html&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;remove old posts - a time-triggered function, invoked once per day, which checks for posts that were published more than 14 days from the day there were created.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's deployed to the Azure Functions via VS Code plugin.&lt;/p&gt;

&lt;h5&gt;
  
  
  GitHub repository
&lt;/h5&gt;

&lt;p&gt;Source code of frontend and backend services. Also, it stores all posts as HTML representation (in the &lt;code&gt;entries.html&lt;/code&gt; file). &lt;/p&gt;

&lt;p&gt;Whenever a new entry is added a new GitHub Action is triggered and it deploys all static resources to the Azure Static Web App service. Only after that, an entry will be visible on a website (that's why you need to wait a couple of minutes to see it on a page).&lt;/p&gt;

&lt;h4&gt;
  
  
  Screenshots
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--j63dU7er--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5leg0n1ppx1y2yz3tdjm.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--j63dU7er--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5leg0n1ppx1y2yz3tdjm.gif" alt="Image description" width="880" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tWOlR1Ab--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den1.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tWOlR1Ab--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den1.PNG" alt="den1" width="742" height="910"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4JE8L_Fi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den2.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4JE8L_Fi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den2.PNG" alt="den2" width="771" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--u4Uy7Vky--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u4Uy7Vky--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den3.PNG" alt="den3" width="768" height="235"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_IEVS081--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_IEVS081--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den4.PNG" alt="den4" width="880" height="661"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TizlnePV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den5.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TizlnePV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/wkrzywiec/classic-den/raw/main/assets/den5.PNG" alt="den5" width="880" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>azuretrialhack</category>
      <category>azure</category>
      <category>serverless</category>
      <category>java</category>
    </item>
    <item>
      <title>Automating quality checks for Kubernetes YAMLs</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Thu, 02 Sep 2021 10:44:43 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/automating-quality-checks-for-kubernetes-yamls-398</link>
      <guid>https://dev.to/wkrzywiec/automating-quality-checks-for-kubernetes-yamls-398</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="//unsplash.com/@tegethoff"&gt;Mark Tegethoff&lt;/a&gt; on &lt;a href="https://unsplash.com" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;If you have ever wondered how to make sure that your YAML Kubernetes objects are defined correctly and are following industry best practices, this blog post is for you. In a few paragraphs, I'll show you how to create a GitHub Actions workflow that will first analyze K8s object definitions using Datree, then deploy it on a cluster and run some tests.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It doesn't matter if you've just started your journey with Kubernetes or you're already an expert in it; writing object definitions is not an easy task. You can very easily make a mistake that could be very costly, if deployed to a production environment. And if you're just starting to learn Kubernetes, you might need help understanding which metadata and specifications should be provided, but, by design, are not mandatory.&lt;/p&gt;

&lt;p&gt;The ideal way to navigate these challenges would be to have an experienced colleague perform a review of your code changes. But sometimes you don't have such a person around, or it's very hard to get feedback from them. And even if you do have an amazing person to help you, it still won't prevent you from making mistakes from time to time. &lt;/p&gt;

&lt;p&gt;Instead, I would like to show you another approach: create a simple, automated quality check of your K8s object definitions using &lt;a href="https://www.datree.io/?utm_source=Wojtek&amp;amp;utm_medium=devto+article&amp;amp;utm_campaign=Automating+quality+check+for+Kubernetes+YAMLs&amp;amp;utm_id=influencer" rel="noopener noreferrer"&gt;Datree.io&lt;/a&gt;, &lt;a href="https://cloud.google.com/kubernetes-engine" rel="noopener noreferrer"&gt;Google Kubernetes Engine&lt;/a&gt; and &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We'll build a GitHub Actions workflow which will be triggered after each change made on the &lt;em&gt;master&lt;/em&gt; branch. The workflow will have two stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Datree&lt;/em&gt; analysis of missing configurations, and quality check,&lt;/li&gt;
&lt;li&gt;Deployment and testing an example application in a real cluster (&lt;em&gt;GKE&lt;/em&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So roll up your sleeves and let's automate it!&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow structure
&lt;/h3&gt;

&lt;p&gt;First of all you need a GitHub repository to hold our YAML files. I'm using my old project - &lt;a href="https://github.com/wkrzywiec/k8s-helm-helmfile" rel="noopener noreferrer"&gt;k8s-helm-helmfile&lt;/a&gt;. This repository has three folders, each containing a different approach to deploy applications into Kubernetes clusters. You can read more about those approaches in my previous blog posts about &lt;a href="https://wkrzywiec.medium.com/deployment-of-multiple-apps-on-kubernetes-cluster-walkthrough-e05d37ed63d1" rel="noopener noreferrer"&gt;vanilla K8s&lt;/a&gt;, &lt;a href="https://wkrzywiec.medium.com/how-to-deploy-application-on-kubernetes-with-helm-39f545ad33b8" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; and &lt;a href="https://medium.com/swlh/how-to-declaratively-run-helm-charts-using-helmfile-ac78572e6088" rel="noopener noreferrer"&gt;helmfile&lt;/a&gt; deployments.&lt;/p&gt;

&lt;p&gt;To keep this blog post short, I'll show how to create a workflow that will use Helm to deploy an application, but you can easily do this with other approaches, such as those mentioned previously.&lt;/p&gt;

&lt;p&gt;The first step is to create a workflow definition file. In the root folder of a repository create a new directory &lt;em&gt;./github/workflows&lt;/em&gt; inside of which there will be a &lt;code&gt;master.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Quality check&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;master'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It contains following specifications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; - name of our workflow&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;on&lt;/code&gt; - a condition based on which a workflow will be triggered. A workflow will only be started when changes are committed on the &lt;code&gt;master&lt;/code&gt; branch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that, you can move on to the best part - defining jobs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Datree analysis
&lt;/h3&gt;

&lt;p&gt;In this part you will use a free tool called &lt;em&gt;Datree&lt;/em&gt;, which analyzes K8s definitions and will stop the workflow if it finds any problems. It's very important to have a safety net like this, so you can feel confident that even if you make a mistake, or aren’t aware of best practices, an assistant will keep you on track. &lt;/p&gt;

&lt;p&gt;Before defining a GitHub workflow, let's install &lt;em&gt;Datree&lt;/em&gt; locally. To do that, go to their &lt;a href="https://www.datree.io/?utm_source=Wojtek&amp;amp;utm_medium=devto+article&amp;amp;utm_campaign=Automating+quality+check+for+Kubernetes+YAMLs&amp;amp;utm_id=influencer" rel="noopener noreferrer"&gt;official website&lt;/a&gt; which will guide you how to install the &lt;em&gt;Datree&lt;/em&gt; CLI. I'm using Linux (or to be precise, WSL), so only the following command is necessary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; curl https://get.datree.io | /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After couple of seconds Datree will be installed. &lt;/p&gt;

&lt;p&gt;To test it out, go to the folder where Kubernetes YAML files are located and run &lt;em&gt;Datree&lt;/em&gt; test command (in my case I'm using a vanilla K8s file from &lt;a href="https://github.com/wkrzywiec/k8s-helm-helmfile" rel="noopener noreferrer"&gt;k8s-helm-helmfile project&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; datree &lt;span class="nb"&gt;test &lt;/span&gt;adminer-deployment.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;where the &lt;code&gt;adminer-deployment.yaml&lt;/code&gt; file is a Kubernetes object definition. &lt;/p&gt;

&lt;p&gt;Here is the output that I got:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m5xi01xgh0e0ksmnqx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m5xi01xgh0e0ksmnqx9.png" alt="datree-test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, &lt;em&gt;Datree&lt;/em&gt; has prepared a short summary of how many rules this YAML file violates. It provides a very useful explanation with a hint on how to fix them.&lt;/p&gt;

&lt;p&gt;Starting from here, you can work on each issue to make these tests pass. It's a great way to learn and practise. But what if you deliberately chose not to comply with some of the rules? Luckily, &lt;em&gt;Datree&lt;/em&gt; gives a possibility to prepare a custom policy, a set of rules against which YAML files will be checked.&lt;/p&gt;

&lt;p&gt;To set up a policy, you need to go to your dashboard. Your personal link to it is located at the end of each scan, in the summary table, in the last row called &lt;code&gt;See all rules in policy&lt;/code&gt; (I've marked it in a previous screenshot). It will take you to a login page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxelxmef6icptu225cgx3.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxelxmef6icptu225cgx3.PNG" alt="login-page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you need to login. For convenience, I would suggest using the GitHub account. After successful authorization you will reach a &lt;em&gt;Policies&lt;/em&gt; page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qbfyn3ahvue92qo1a5d.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7qbfyn3ahvue92qo1a5d.PNG" alt="datree-policies"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can inspect all the rules that you can turn on and off. By default, only some of them are enabled. To activate or deactivate them, use the toggle button visible next to the rule name.&lt;/p&gt;

&lt;p&gt;If a name of a rule is too enigmatic, you can check its details by clicking on its name. It will then show more information and a sample output when this rule is violated. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89zc7kpto2byjjso0thd.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F89zc7kpto2byjjso0thd.PNG" alt="datree-rule"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another cool feature is that you can define your own tips on how to fix a problem. The defaults should be enough, but if you prefer to extend it, view it in your own language, or add a link to a blog post, Stack Overflow discussion or any other online material, you can put it here by simply clicking an &lt;em&gt;Edit&lt;/em&gt; button.&lt;/p&gt;

&lt;p&gt;Finally, you can create your own set of rules, known as policies, to run for different applications, environments and stages. Simply click the &lt;em&gt;Create Policy&lt;/em&gt; button at the top of the page.&lt;/p&gt;

&lt;p&gt;Let's now check the second page, available on the left panel, called &lt;em&gt;History&lt;/em&gt;. As the name might suggest, here you can see a convenient summary of all previous test runs. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69nkkly5k1ff1q6abs3d.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F69nkkly5k1ff1q6abs3d.PNG" alt="datree-history"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before writing this post, I’d already played around with &lt;em&gt;Datree&lt;/em&gt;, which is why I already have several test runs listed in the history panel, but in your case you should have only one.&lt;/p&gt;

&lt;p&gt;That would be it for a quick tour around &lt;em&gt;Datree&lt;/em&gt; dashboard! &lt;/p&gt;

&lt;p&gt;Now let's build a GitHub Actions workflow. &lt;/p&gt;

&lt;p&gt;First, provide your &lt;em&gt;Datree&lt;/em&gt; token as an environment variable in a workflow. To achieve this, click on your avatar in the top right corner of the &lt;em&gt;Datree&lt;/em&gt; dashboard, then click &lt;strong&gt;Settings&lt;/strong&gt;. It'll guide you to the page where you can find your token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2p6f2ahpdtd3bwww5lw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp2p6f2ahpdtd3bwww5lw.png" alt="datree-token"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ve copied the token, go to your GitHub project/repository’s &lt;strong&gt;Settings&lt;/strong&gt; page. Then select &lt;strong&gt;Secrets&lt;/strong&gt; and click the &lt;strong&gt;New repository secret&lt;/strong&gt; button. &lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Name&lt;/strong&gt; field put &lt;code&gt;DATREE_TOKEN&lt;/code&gt; and in the &lt;strong&gt;Value&lt;/strong&gt; field a token copied from &lt;em&gt;Datree&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrd2msj27pjevkwqe0vs.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdrd2msj27pjevkwqe0vs.PNG" alt="github-secret"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can move on to the workflow's definition file and define the first job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;datree&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Validate Helm charts&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dtzar/helm-kubectl:3.6.3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's called &lt;code&gt;datree&lt;/code&gt;, it runs on the latest Ubuntu inside a &lt;a href="https://hub.docker.com/r/dtzar/helm-kubectl/" rel="noopener noreferrer"&gt;dtzar/helm-kubectl&lt;/a&gt; Docker container. I've selected this setup because I would like to run tests against Helm release (and not vanilla K8s as it was done previously). The reason to choose this Docker image is because it contains necessary dependencies (K8s and Helm), so I can skip their installation and make my workflow faster.&lt;/p&gt;

&lt;p&gt;Let’s define the next three steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout 🛎️&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Datree 🔨&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;helm plugin install https://github.com/datreeio/helm-datree&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Datree test 🔥&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;DATREE_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.DATREE_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;helm datree test ./helm/app -- --values ./helm/adminer.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the first one, &lt;code&gt;actions/checkout@v2&lt;/code&gt;, you get code from the repository. In step two, you instal the &lt;a href="https://hub.datree.io/helm-plugin?utm_source=Wojtek&amp;amp;utm_medium=devto+article&amp;amp;utm_campaign=Automating+quality+checks+for+Kubernetes+YAMLs&amp;amp;utm_id=influencer" rel="noopener noreferrer"&gt;Datree Helm plugin&lt;/a&gt; to run &lt;em&gt;Datree&lt;/em&gt; tests using Helm. In the third step, you run actual tests using a Helm CLI. The &lt;code&gt;DATREE_TOKEN&lt;/code&gt; environment variable is added to it so the result will be linked with a &lt;em&gt;Datree&lt;/em&gt; account. &lt;/p&gt;

&lt;p&gt;Finally, in an actual run script, I provide the location of a Helm template and the location of testing &lt;code&gt;values.yaml&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;After that you can commit changes and push it to GitHub. It should trigger a workflow, which will be available in the &lt;strong&gt;Actions&lt;/strong&gt; tab.  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7xh2v531ulost9ky22r.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp7xh2v531ulost9ky22r.PNG" alt="github-datree-fail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my case, several tests are failing, demonstrated by the failing status of a workflow. To find out more, click on the failing job. It will take you to the console output, where you can investigate what went wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xe3r20z44t3os1cr3po.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xe3r20z44t3os1cr3po.PNG" alt="github-datree-fail-details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As well as checking policy check result(s) in the workflow's console, you can go back to &lt;a href="https://app.datree.io/cli/invocations" rel="noopener noreferrer"&gt;the History page&lt;/a&gt; in the &lt;em&gt;Datree&lt;/em&gt; dashboard and analyze errors there.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0a2rrs2m1cba0ta6jy2.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp0a2rrs2m1cba0ta6jy2.PNG" alt="datree-fail"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have a similar screen to mine, stop here, correct mistakes and push changes to GitHub. If you feel that some rules are obsolete for you, turn them off in the &lt;em&gt;Datree&lt;/em&gt; dashboard, but do not turn off too many!&lt;/p&gt;

&lt;p&gt;After fixing all the issues and re-running the workflow, the previously marked red exes [X] will become green check marks [V], indicating that the workflow has passed the validation and policy check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ffa7tcuzol6r1l6v2og.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ffa7tcuzol6r1l6v2og.PNG" alt="github-datree-success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And some details about a job:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib5lwgs6flr3wczw41b4.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fib5lwgs6flr3wczw41b4.PNG" alt="github-datree-success-details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The successful result will be also visible in the &lt;em&gt;Datree&lt;/em&gt; dashboard:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h8ge9rhf8lv79exyiuo.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4h8ge9rhf8lv79exyiuo.PNG" alt="datree-success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! We can now move on to the next part.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing on GKE
&lt;/h3&gt;

&lt;p&gt;After making sure that templates are okay from &lt;em&gt;Datree&lt;/em&gt;’s point of view, move to deploying them to a real Kubernetes cluster (which is not production) and then check if everything is working there, e.g. if a website is available over the internet, etc.&lt;/p&gt;

&lt;p&gt;First step would be to have a cluster. I've picked &lt;em&gt;Google Kubernetes Engine&lt;/em&gt; (&lt;em&gt;GKE&lt;/em&gt;) because of its &lt;a href="https://cloud.google.com/free/docs/gcp-free-tier/#kubernetes-engine" rel="noopener noreferrer"&gt;free tier&lt;/a&gt;, but if you have your own cluster (on AWS or any other cloud provider) just use it.&lt;/p&gt;

&lt;p&gt;Right now I'll follow with steps that are necessary to set up a job for &lt;em&gt;GKE&lt;/em&gt;, so if you already have a &lt;em&gt;GKE&lt;/em&gt; or any other Kubernetes cluster up and running, skip this part.&lt;/p&gt;

&lt;p&gt;Before adding a new job to a workflow, you need to set up couple of things in &lt;em&gt;GKE&lt;/em&gt; (these instructions are based on official &lt;a href="https://cloud.google.com/kubernetes-engine/docs/quickstart" rel="noopener noreferrer"&gt;GKE Quickstart guide&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a Google Cloud account. &lt;a href="https://cloud.google.com" rel="noopener noreferrer"&gt;You can start from the main page&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Create or select an already existing Google Cloud project. It can be done either from &lt;a href="https://console.cloud.google.com/home/" rel="noopener noreferrer"&gt;Google Cloud console&lt;/a&gt; or &lt;a href="https://console.cloud.google.com/projectselector2/home/dashboard" rel="noopener noreferrer"&gt;from Google project selector&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Enable billing for a project. You will need to provide your credit card information, &lt;a href="https://cloud.google.com/billing/docs/how-to/modify-project" rel="noopener noreferrer"&gt;like it's described here&lt;/a&gt;, but don't worry, you won't be charged anything. This step is just to verify that you're a human.&lt;/li&gt;
&lt;li&gt;Enable &lt;em&gt;GKE&lt;/em&gt; APIs, which &lt;a href="https://console.cloud.google.com/flows/enableapi?apiid=artifactregistry.googleapis.com,container.googleapis.com" rel="noopener noreferrer"&gt;can be done on this page&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Until now, all configuration was carried out in a web browser, but now you need to move to either &lt;a href="https://cloud.google.com/shell" rel="noopener noreferrer"&gt;Google Cloud Shell&lt;/a&gt;, or &lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;install Cloud SDK&lt;/a&gt; and follow the instructions in a terminal:&lt;/p&gt;

&lt;p&gt;Set up basic configuration in &lt;code&gt;gcloud&lt;/code&gt; tool, like default project, region and zone. In my case, the project name is &lt;code&gt;k8s-helm-helmfile&lt;/code&gt; and the region is &lt;code&gt;europe-central2&lt;/code&gt;, but for you it may be different.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project k8s-helm-helmfile

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;compute/region europe-central2

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;compute/zone europe-central2-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create an &lt;a href="https://cloud.google.com/iam/docs/service-accounts" rel="noopener noreferrer"&gt;IAM Service Account&lt;/a&gt;, which is an account which will be used in GitHub Action workflow. I've called mine &lt;code&gt;helm-github-actions-service&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud iam service-accounts create helm-github-actions-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get an email from the newly created Service Account. You will need it for the next step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud iam service-accounts list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assign roles to a Service Account, where &lt;code&gt;&amp;lt;EMAIL&amp;gt;&lt;/code&gt; tag is taken from previous step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud projects add-iam-policy-binding k8s-helm-helmfile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serviceAccount:&amp;lt;EMAIL&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/container.admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/storage.admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/container.clusterAdmin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/iam.serviceAccountUser &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/container.developer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Export the Service Account Key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; gcloud iam service-accounts keys create key.json &lt;span class="nt"&gt;--iam-account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;EMAIL&amp;gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GKE_SA_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;key.json | &lt;span class="nb"&gt;base64&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add it as a GitHub Secret to a project (the same way as for &lt;code&gt;DATREE_TOKEN&lt;/code&gt;) with &lt;code&gt;GKE_SA_KEY&lt;/code&gt; as key. To see the value of the exported key you can use the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;printenv &lt;/span&gt;GKE_SA_KEY
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is set up, now go back to workflow definition where you first created a Kubernetes cluster on &lt;em&gt;GKE&lt;/em&gt;, deploy a sample Helm release, and test it. &lt;/p&gt;

&lt;p&gt;First create a new job called &lt;code&gt;gke&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;gke&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test Helm chart on GKE&lt;/span&gt; 
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;datree&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k8s-helm-helmfile&lt;/span&gt;
      &lt;span class="na"&gt;GKE_CLUSTER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;helm-test&lt;/span&gt;
      &lt;span class="na"&gt;GKE_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;europe-central2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to the previous example, you have &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;runs-on&lt;/code&gt; configurations. There is also a &lt;code&gt;needs&lt;/code&gt; configuration, which means that in order to run this job, the &lt;code&gt;datree&lt;/code&gt; job needs to first be completed successfully. This prevents spinning up clusters and deploying a sample application if something goes wrong during a &lt;em&gt;Datree&lt;/em&gt; check. The last part of job configuration are environment variables (&lt;code&gt;env&lt;/code&gt;), which will be used in workflow steps. They're my GCP project id, K8s cluster name, and my &lt;em&gt;GKE&lt;/em&gt; region.&lt;/p&gt;

&lt;p&gt;Moving on to the next steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout 🛎️&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v2&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup gcloud CLI ⚡&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google-github-actions/setup-gcloud@master&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;service_account_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GKE_SA_KEY }}&lt;/span&gt;
      &lt;span class="na"&gt;project_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.PROJECT_ID }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first one is for getting code from the project.&lt;br&gt;
The second is to configure (login and set up project) Google Cloud CLI which will be used in following steps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Create Autopilot GKE cluster 🔨&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;gcloud container clusters create-auto ${{ env.GKE_CLUSTER }} \&lt;/span&gt;
    &lt;span class="s"&gt;--project=${{ env.PROJECT_ID }} \&lt;/span&gt;
    &lt;span class="s"&gt;--region=${{ env.GKE_REGION }}&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Config kubectl for GKE cluster ⚡&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;google-github-actions/get-gke-credentials@main&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;cluster_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.GKE_CLUSTER }}&lt;/span&gt;
      &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.GKE_REGION }}&lt;/span&gt;
      &lt;span class="na"&gt;credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GKE_SA_KEY }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above steps create a new GKE cluster and configure kubectl so it's connected with the newly created cluster. With that, you can move on to the step where an adminer Helm release will be installed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy test Helm release 🚀&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deliverybot/helm@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;release&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer&lt;/span&gt;
    &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;default&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./helm/app&lt;/span&gt;
    &lt;span class="na"&gt;helm&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;helm3&lt;/span&gt;
    &lt;span class="na"&gt;value-files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./helm/adminer.yaml&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;app:&lt;/span&gt;
          &lt;span class="s"&gt;service:&lt;/span&gt;
            &lt;span class="s"&gt;type: LoadBalancer&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop here and analyze what's going on. First of all, you’re using the &lt;a href="https://github.com/deliverybot/helm" rel="noopener noreferrer"&gt;deliverybot/helm&lt;/a&gt; GitHub Actions, which provides a convenient way to use Helm. By adding a few parameters, you can deploy an application onto a Kubernetes cluster. The entire list of available parameters can be found on &lt;a href="https://github.com/deliverybot/helm" rel="noopener noreferrer"&gt;the official website&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;In the above example, I've used the following steps alone:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;release&lt;/code&gt; - a release name,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;namespace&lt;/code&gt; - specifies the K8s namespace where the app will be installed,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chart&lt;/code&gt; - gives information about the location of a Helm chart,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;helm&lt;/code&gt; - indicates which version of Helm will be used,&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;value-files&lt;/code&gt; - file used to override the default values from a Helm chart, in my case it's an Adminer's values.yaml file (the Helm chart I use for testing, which deploys popular database client - &lt;a href="https://www.adminer.org" rel="noopener noreferrer"&gt;Adminer&lt;/a&gt;),&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;values&lt;/code&gt; - this parameter works pretty the same as previous one - it's used to override the default values from a Helm chart, but instead of doing it with a file, we can directly specify values that need to be overridden; here, I'm overriding only the Service kind, as by default it's a &lt;code&gt;ClusterIP&lt;/code&gt;, but I don't want to change it in the adminer.yaml file.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After successful installation, our workflow can proceed with tests. I've decided to run a very simple test, which only checks if a page is opening, but you can of course build a more sophisticated test chain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test installed application 🔥&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
&lt;span class="s"&gt;export IP_ADDRESS=$(kubectl get services -o=jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}')&lt;/span&gt;
    &lt;span class="s"&gt;echo "$IP_ADDRESS"&lt;/span&gt;
    &lt;span class="s"&gt;curl http://"$IP_ADDRESS":8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line in a testing script is for finding out under which IP address the adminer application is exposed. The second is for debugging, and the last for actual testing.&lt;/p&gt;

&lt;p&gt;Once the tests are done, you need to destroy a cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Delete cluster GKE Cluster 💥&lt;/span&gt;
  &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ always() }}&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;gcloud container clusters delete ${{ env.GKE_CLUSTER }} --zone=${{ env.GKE_REGION }} --quiet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;if: ${{ always() }}&lt;/code&gt; part is very important here. It makes sure that even if any of the previous steps fail, this one will always run. Otherwise you could end up with a bill from Google at the end of a month.&lt;/p&gt;

&lt;p&gt;After commiting changes and pushing them to GitHub, a workflow will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F184zx9c54jvoa2daaayz.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F184zx9c54jvoa2daaayz.PNG" alt="github-full"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First a &lt;em&gt;Datree&lt;/em&gt; step is executed, then the installation is done on &lt;em&gt;GKE&lt;/em&gt;. To check the details of the second step, click on its name. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu83t5ktcwkk2j7oamqd.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyu83t5ktcwkk2j7oamqd.PNG" alt="github-gke-success-details"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;And that's it for today! I hope that this blog post encourages you to build something like this on your own, give &lt;em&gt;Datree&lt;/em&gt; (or any other static code analysis tool) a try, and set up a cluster for automated tests, so you will feel more confident about the changes you made in your code base. It can all be set up and operational in a flash.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>datree</category>
      <category>github</category>
      <category>googlecloud</category>
    </item>
    <item>
      <title>How to write good quality Python code with GitHub Actions</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Sat, 06 Mar 2021 15:53:33 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/how-to-write-good-quality-python-code-with-github-actions-2gci</link>
      <guid>https://dev.to/wkrzywiec/how-to-write-good-quality-python-code-with-github-actions-2gci</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@deko_lt"&gt;Valdemaras D.&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;In this blog post I want to share with you how you can set up GitHub Actions workflows in your Python project to make sure that the code you write is elegant, aligned with all best practices and it’s well tested.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Lately I’ve decided to start to learn Python. Nowadays it’s one of the most popular programming language so it was no-brainer decision to try it out. Especially that I’m still bad at algorithms so I was hoping that I’ll kill two birds with one stone — learn Python and practice it with solving some algorithmic exercises.&lt;/p&gt;

&lt;p&gt;Therefore I’ve created a &lt;a href="https://github.com/wkrzywiec/algorithms-python"&gt;Algorithms Python&lt;/a&gt; project on GitHub where I put solutions to problems that I found interesting.&lt;/p&gt;

&lt;p&gt;But when I was practicing it I’ve stomp on a problem.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How to make sure that the code I write has high quality? How can I make sure that my code is aligned with official style guide &lt;a href="https://www.python.org/dev/peps/pep-0008/"&gt;PEP 8&lt;/a&gt; ? And how to check if it’s well tested?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well there is a solution! &lt;strong&gt;GitHub Actions&lt;/strong&gt;!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But what exactly I want to achieve?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’ll show you how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;set up integration with &lt;a href="https://www.codefactor.io"&gt;&lt;strong&gt;CodeFactor&lt;/strong&gt;&lt;/a&gt; — tool that founds potential bugs in your code,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;set up &lt;a href="https://wemake-python-stylegui.de/en/latest/pages/usage/integrations/github-actions.html"&gt;&lt;strong&gt;wemake-python-styleguide&lt;/strong&gt;&lt;/a&gt; GitHub Actions workflow that checks if your code is aligned with PEP 8,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;run unit tests and report its’ coverage with &lt;a href="https://codecov.io"&gt;&lt;strong&gt;Codecov&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the next steps I assume that you already has a Python project on GitHub. If you don’t have it create one 😉.&lt;/p&gt;

&lt;h3&gt;
  
  
  CodeFactor
&lt;/h3&gt;

&lt;p&gt;It’s a very simple tool that check the quality of you code. It’s free of charge for all public repositories and for 1 private.&lt;/p&gt;

&lt;p&gt;To start with it, go to the official website — &lt;a href="http://www.codefactor.io/"&gt;www.codefactor.io&lt;/a&gt;, and create a new account by logging via GitHub. The icon for doing that should be located in the top right corner of the main page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vGNGJSZR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ked9zf4ki0xqcyfmbexm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vGNGJSZR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ked9zf4ki0xqcyfmbexm.png" alt="Alt Text" width="880" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you’ll be registered add a repository to your dashboard by clicking the plus ( + ) sign located at the top right corner.&lt;/p&gt;

&lt;p&gt;From the list pick the repository that you want to analyse and click &lt;strong&gt;Import&lt;/strong&gt; at the bottom of the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zcFiXB9Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/schitvgm9v6w1l88z1xy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zcFiXB9Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/schitvgm9v6w1l88z1xy.png" alt="Alt Text" width="880" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it! You should be now redirected to a dashboard with a list of all issues that has been found. Awesome!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kAqLs72B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4omswfir0ivyv4164tx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kAqLs72B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4omswfir0ivyv4164tx9.png" alt="Alt Text" width="880" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;wemake-python-styleguide&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s move on to the second tool which will check if a code is written accordingly to the official Python style guide.&lt;/p&gt;

&lt;p&gt;This time we won’t create an account in any web service. We’ll set up a &lt;em&gt;GitHub Actions&lt;/em&gt; workflow which will be triggered whenever a &lt;em&gt;pull request&lt;/em&gt; is created and will add comments to a review where it founds potential problems.&lt;/p&gt;

&lt;p&gt;To those of you who don’t know what &lt;a href="https://github.com/features/actions"&gt;&lt;em&gt;GitHub Actions&lt;/em&gt;&lt;/a&gt; is. It’s a new feature on GitHub that allows to automate many tasks and it’s usually treated as CI/CD tool (&lt;em&gt;Continuous Integration/Continuous Deployment&lt;/em&gt;) which runs tests, runs quality checks and then deploy it. But it’s not the only purpose of it.&lt;/p&gt;

&lt;p&gt;To start with it, in the root folder of your project create a &lt;code&gt;.github/workflows&lt;/code&gt; folder where definitions of your workflows will be located.&lt;/p&gt;

&lt;p&gt;Then create a new file with a name &lt;code&gt;workflow-pr.yaml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Python Pull Request Workflow&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="na"&gt;qa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Quality check&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.8&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run unit tests&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pip install pytest&lt;/span&gt;
          &lt;span class="s"&gt;pytest&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wemake Python Stylguide&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wemake-services/wemake-python-styleguide@0.13.4&lt;/span&gt;
        &lt;span class="na"&gt;continue-on-error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;github-pr-review'&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s a very simple workflow with the full &lt;code&gt;name&lt;/code&gt; &lt;code&gt;Python Pull Request Workflow&lt;/code&gt;. It’s triggered &lt;code&gt;on&lt;/code&gt; each &lt;code&gt;pull_request&lt;/code&gt;, so whenever we create new or update existing one following &lt;code&gt;jobs&lt;/code&gt; will run.&lt;/p&gt;

&lt;p&gt;Above workflow consists only one — &lt;code&gt;qa&lt;/code&gt; — job that has 4 steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;actions/checkout@v1&lt;/code&gt; — is necessary to let GitHub Actions workflow know that it can use code located in a repository,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Set up Python&lt;/code&gt; that uses &lt;code&gt;actions/setup-python@master&lt;/code&gt; configure a Python version, in our case it’s &lt;code&gt;python-version: 3.8&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Run unit tests&lt;/code&gt; runs all unit tests located in the project. For that I’m using &lt;a href="https://docs.pytest.org/en/latest/"&gt;&lt;strong&gt;pytest&lt;/strong&gt;&lt;/a&gt; which first needs to be installed &lt;code&gt;pip install pytest&lt;/code&gt; so the next command &lt;code&gt;pytest&lt;/code&gt; can be run. If any test fails on this step the next one will not run.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Wemake Python Styleguide&lt;/code&gt; step is the one that we’re the most interested in. It uses the &lt;code&gt;wemake-services/wemake-python-styleguide@0.13.4&lt;/code&gt; action which are the atomic building blocks of the workflows. You can found them on the &lt;a href="https://github.com/marketplace?type=actions"&gt;GitHub Marketplace&lt;/a&gt;, like &lt;a href="https://github.com/marketplace/actions/wemake-python-styleguide"&gt;mentioned action&lt;/a&gt;. This one is configured (&lt;code&gt;with&lt;/code&gt; clause) to use &lt;code&gt;github-pr-review&lt;/code&gt; &lt;strong&gt;reporter&lt;/strong&gt; which enables the inline comments in the code review. More supported reporter options could be found on the &lt;a href="https://wemake-python-stylegui.de/en/latest/pages/usage/integrations/github-actions.html"&gt;official website&lt;/a&gt;. Finally this workflow requires to pass your &lt;code&gt;GIHUB_TOKEN&lt;/code&gt; and that’s why the &lt;code&gt;env&lt;/code&gt; clause is added.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test it running you need to create a new branch, commit some changes and push it to the GitHub. Then create a &lt;em&gt;pull request&lt;/em&gt; which will trigger this workflow. To check it go to the &lt;strong&gt;Actions&lt;/strong&gt; tab in your project and if everything went fine it should look like this one:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fMcvIJQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9bf0xdz1el8jwdi98otp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fMcvIJQA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9bf0xdz1el8jwdi98otp.png" alt="Alt Text" width="615" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on &lt;em&gt;Run unit tests&lt;/em&gt;, in the console log, you’ll see the test report:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PO5WHFAE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9fg2gd542oga3tpivme.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PO5WHFAE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x9fg2gd542oga3tpivme.png" alt="Alt Text" width="793" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you go back to the Pull request you should see comments added. Like here: &lt;a href="https://github.com/wkrzywiec/algorithms-python/pull/6"&gt;https://github.com/wkrzywiec/algorithms-python/pull/6&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bxKgjgLf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o1v592q8biktvyog5um.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bxKgjgLf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1o1v592q8biktvyog5um.png" alt="Alt Text" width="818" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Codecov
&lt;/h3&gt;

&lt;p&gt;And finally we want to have a test report with coverage. For that we again use &lt;em&gt;pytest&lt;/em&gt; library which will generate it for us and then we upload it to the &lt;em&gt;Codecov&lt;/em&gt; which will take care of visualizing it.&lt;/p&gt;

&lt;p&gt;Before defining a new workflow first you need to create an account at &lt;em&gt;Codecov&lt;/em&gt;. Therefore go to &lt;a href="https://codecov.io"&gt;https://codecov.io&lt;/a&gt; at click &lt;strong&gt;Sign Up&lt;/strong&gt; button located at the top right corner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nzqxTmxR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btlymy1ukufmwzbdn8jq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nzqxTmxR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btlymy1ukufmwzbdn8jq.png" alt="Alt Text" width="880" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then choose GitHub as a sign up option.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XvRUIkWx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cbugz8rxc7hcnu2yzqzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XvRUIkWx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cbugz8rxc7hcnu2yzqzu.png" alt="Alt Text" width="880" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you should be taken to your dashboard for GitHub projects, where you need to click the &lt;strong&gt;Add new repository&lt;/strong&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2k_nUGTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqazwagad4bb80wkqsi7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2k_nUGTi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nqazwagad4bb80wkqsi7.png" alt="Alt Text" width="880" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A list of all your projects will appear from where you can pick the one you want to analyze.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KIkWMEr_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xk72rnsygi9j81ia9g9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KIkWMEr_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9xk72rnsygi9j81ia9g9.png" alt="Alt Text" width="880" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then a page with your token will appear. Save it, because we will use it in next step.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZTSdO2wz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ffvbyfzbhgkfx3j3ed9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZTSdO2wz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ffvbyfzbhgkfx3j3ed9.png" alt="Alt Text" width="880" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now go back to the project on GitHub and click on its &lt;strong&gt;Settings&lt;/strong&gt; button. There click &lt;strong&gt;Secrets&lt;/strong&gt;, and then &lt;strong&gt;Add a new secret&lt;/strong&gt; where you can provide the token you’ve generated on &lt;em&gt;Codecov&lt;/em&gt; website. To finalize it click &lt;strong&gt;Add secret&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ndNMeIt6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9hxupo073zxsl4uodqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ndNMeIt6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p9hxupo073zxsl4uodqt.png" alt="Alt Text" width="880" height="594"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, everything is set up, so we can move on to defining the GitHub workflow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Python Master Workflow&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;master'&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;codecov&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Codecov Workflow&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-18.04&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v1&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3.8&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Generate coverage report&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pip install pytest&lt;/span&gt;
          &lt;span class="s"&gt;pip install pytest-cov&lt;/span&gt;
          &lt;span class="s"&gt;pytest --cov=./ --cov-report=xml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload coverage to Codecov&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codecov/codecov-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.CODECOV_TOKEN }}&lt;/span&gt;
          &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./coverage.xml&lt;/span&gt;
          &lt;span class="na"&gt;flags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unittests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again we create a separate file called &lt;code&gt;workflow-master.yaml&lt;/code&gt;, because this time we don’t want to trigger this workflow when &lt;em&gt;pull request&lt;/em&gt; is created. This one should run only when a new commit is &lt;code&gt;pushe&lt;/code&gt;d on the &lt;code&gt;master branch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;jobs&lt;/code&gt; section there is only one job called &lt;code&gt;codecov&lt;/code&gt; which consists of 4 steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;uses: actions/checkout@v1&lt;/code&gt; — this one, again, is just to tell GitHub Actions that we want to use the files located in current repository,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;uses: actions/setup-python@master&lt;/code&gt; — this one was also mentioned before, here we set up a Python version, which is &lt;code&gt;3.8&lt;/code&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;then there is a new step, which is responsible for &lt;code&gt;Generate coverage report&lt;/code&gt;; it’s a series of scripts which cover installing &lt;em&gt;pytests&lt;/em&gt; (&lt;code&gt;pip install pytest&lt;/code&gt;), &lt;em&gt;pytest-cov&lt;/em&gt; (&lt;code&gt;pip install pytest-cov&lt;/code&gt;) and running actual tests (&lt;code&gt;pytest — cov=./ — cov-report=xml&lt;/code&gt;),&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;and finally the generated test coverage report can be uploaded to the Codecov (&lt;code&gt;Upload coverage to Codecov&lt;/code&gt;). Here we uses: &lt;code&gt;codecov/codecov-action@v1&lt;/code&gt; &lt;a href="https://github.com/marketplace/actions/codecov"&gt;GitHub Action available on the Marketplace&lt;/a&gt;. In it we provide 3 arguments: &lt;code&gt;token: ${{ secrets.CODECOV_TOKEN }}&lt;/code&gt; which value is taken from the GitHub Secrets vault where we put it, &lt;code&gt;file: ./coverage.xml&lt;/code&gt; is location of the test coverage report (generated step before) and &lt;code&gt;flags: unittests&lt;/code&gt; is a flag that groups our unit tests.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To test it you need to push some commits to the &lt;code&gt;master&lt;/code&gt; branch. It can be done directly from your local repository or by merging a &lt;em&gt;pull request&lt;/em&gt;. If everything went fine, it should like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QulTBX8---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uqbu941ao4vdvsxuxoe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QulTBX8---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9uqbu941ao4vdvsxuxoe.png" alt="Alt Text" width="880" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now if you go back to the &lt;em&gt;Codecov&lt;/em&gt; to your project dashboard you should see similar output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZEBqJhg6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m079986aywr06w79b6gl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZEBqJhg6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m079986aywr06w79b6gl.png" alt="Alt Text" width="730" height="897"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All right, we did it! 🍾&lt;/p&gt;

&lt;p&gt;Just before the wrap up I want to tell you that &lt;em&gt;CodeFactor&lt;/em&gt;, &lt;em&gt;Codecov&lt;/em&gt; or &lt;em&gt;wemake-python-styleguide&lt;/em&gt; are not the only tools that can help you write better quality code. In fact there plenty of them, like &lt;a href="https://sonarcloud.io"&gt;SonarCloud&lt;/a&gt;, &lt;a href="https://github.com/marketplace/actions/github-action-for-pylint"&gt;Pylint&lt;/a&gt;, &lt;a href="https://github.com/marketplace/coveralls"&gt;Coveralls&lt;/a&gt;, &lt;a href="https://github.com/marketplace/deepsource-io"&gt;DeepSource&lt;/a&gt; and more. Some of them could be found on GitHub Marketplace, which is best place to start if you don’t like those what I have proposed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;With this blog post I hope I’ve gave you an idea of how you can set up your GitHub repository to make sure that code you write has good quality. With such tools set you can find all the bugs and vulnerabilities, but keep in mind that not every bug, not every issue is worth to be taken care of. Sometimes it’s better to focus on the real work, not on tidying smelly code base 😉.&lt;/p&gt;

&lt;p&gt;As usual, you can find mentioned project &lt;a href="https://github.com/wkrzywiec/algorithms-python"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>python</category>
      <category>github</category>
      <category>codequality</category>
      <category>project</category>
    </item>
    <item>
      <title>Write better code with SonarQube</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Sun, 28 Feb 2021 14:35:31 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/write-better-code-with-sonarqube-4cdh</link>
      <guid>https://dev.to/wkrzywiec/write-better-code-with-sonarqube-4cdh</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover photo by &lt;a href="https://unsplash.com/@oks_ong95" rel="noopener noreferrer"&gt;Khai Sze Ong&lt;/a&gt; on &lt;a href="https://unsplash.com" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;In this blog post I introduce a SonnarQube, a static code analytic tool, which can help you write more secured, less buggy and cleaner code. I show how to run it and play around with it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you recently started to code in your free time? Or maybe you’re a full-time senior developer with couple years experience? No matter how long you’re in software engineering sometimes it’s hard to write nice and clean code. Especially when you’re working on a complex project.&lt;/p&gt;

&lt;p&gt;The best solution would be to program in pairs with another developer or ask her/him to check what you’ve written in a code review. But sometime you don’t have anyone around to help you. Luckily there is solution for this problem — &lt;em&gt;static code analysis&lt;/em&gt; tool. Of course it won’t be the same as human, but still can help you a lot with your work.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;But wait, what’s static code analysis tool?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Based on special algorithms these tools analyze the code we write and look for bugs, possible security breaches, code smells and presents it in the some kind of report that helps us, developers, find issues in our code. Moreover they can check the unit test coverage, to make sure that all crucial parts of the logic are properly tested.&lt;/p&gt;

&lt;p&gt;Such tools not only help developers find possible mistakes, but they also allows companies to become more into DevOps culture, where programmers get instant feedback about their code quality and prevent moving possible vulnerable feature into production environment.&lt;/p&gt;

&lt;p&gt;An example of such tools (for Java) are: &lt;a href="http://findbugs.sourceforge.net/" rel="noopener noreferrer"&gt;Findbugs&lt;/a&gt;, &lt;a href="https://pmd.github.io/" rel="noopener noreferrer"&gt;PMD&lt;/a&gt; and &lt;a href="https://www.sonarqube.org/" rel="noopener noreferrer"&gt;SonarQube&lt;/a&gt;. And I want to talk about the last one more briefly in this blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  SonarQube
&lt;/h2&gt;

&lt;p&gt;To learn about all its features let’s install it and check on some of my project. Therefore you need to have an instance of &lt;em&gt;SonarQube Community Edition&lt;/em&gt; up and running on your local machine.&lt;/p&gt;

&lt;p&gt;I prefer to use Docker image for that (I’ve recently try dockerize everything), but you can go with regular installation from a zip file. The instructions are available on &lt;a href="https://docs.sonarqube.org/latest/setup/get-started-2-minutes/" rel="noopener noreferrer"&gt;the official website&lt;/a&gt;. All mine instructions are valid also with this approach (you only need to skip the Docker part).&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;In order to follow my instructions you will need install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Docker&lt;/strong&gt; — instructions for &lt;a href="https://docs.docker.com/install/linux/docker-ce/ubuntu/" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt; , &lt;a href="https://docs.docker.com/docker-for-windows/install/" rel="noopener noreferrer"&gt;Windows&lt;/a&gt; , &lt;a href="https://docs.docker.com/docker-for-mac/install/" rel="noopener noreferrer"&gt;Mac&lt;/a&gt; (if you’re on Ubuntu, you’ll need also to &lt;a href="https://docs.docker.com/compose/install/" rel="noopener noreferrer"&gt;install Docker Compose separately&lt;/a&gt;),&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maven&lt;/strong&gt; — &lt;a href="https://maven.apache.org/install.html" rel="noopener noreferrer"&gt;official installation guideline&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;

&lt;p&gt;Once you’ve got installed all prerequisites we can move to setting up the SonarQube Docker container. I would like to use &lt;code&gt;sonarqube:7.9-community&lt;/code&gt; Docker image, but there are also two additional things that I would like to configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;by default, all the analysis are saved in H2 database, I want to change it, so all of them will be stored in the PostgreSQL,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;I would like to enable SonarQube Server configuration with &lt;a href="https://docs.sonarqube.org/latest/analysis/analysis-parameters/" rel="noopener noreferrer"&gt;sonar.properties&lt;/a&gt; file.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As the result I’ve came up with following structure of a folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    develop-env/
    ├─ docker-compose.yml
    └── sonar/
       ├── Dockerfile
       └── sonar.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;First, let’s focus on &lt;strong&gt;sonar&lt;/strong&gt; folder, which contains a &lt;em&gt;Dockerfile&lt;/em&gt;(recipe for an image):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; sonarqube:7.9-community&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; sonar.properties /opt/sonarqube/conf/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;and &lt;em&gt;sonar.properties&lt;/em&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;sonar.junit.reportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;target/surefire-reports&lt;/span&gt;
&lt;span class="py"&gt;sonar.coverage.jacoco.xmlReportPaths&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;target/site/jacoco/jacoco.xml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Dockerfile is pretty straightforward, it uses official &lt;code&gt;sonarqube:7.9-community&lt;/code&gt; image and copy paste the &lt;em&gt;sonar.properties&lt;/em&gt; file into it. The second file is responsible for telling Sonar where to look for reports related to test coverage.&lt;/p&gt;

&lt;p&gt;Next, let’s move on to &lt;em&gt;docker-compose.yml&lt;/em&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarqube&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./sonar&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;9000:9000&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonarnet&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonar.jdbc.url=jdbc:postgresql://sonar-db:5432/sonar&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonarqube_conf:/opt/sonarqube/conf&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonarqube_data:/opt/sonarqube/data&lt;/span&gt;
    &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonar-db&lt;/span&gt;

  &lt;span class="na"&gt;sonar-db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6-alpine&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;sonarnet&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=sonar&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=sonar&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgresql:/var/lib/postgresql&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgresql_data:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarnet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bridge&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarqube_conf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sonarqube_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgresql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgresql_data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In a nutshell it tells Docker to create two containers: &lt;code&gt;sonarqube&lt;/code&gt; and &lt;code&gt;sonar-db&lt;/code&gt;. First one is an instance of a SonarQube Server, and a second one is its database.&lt;/p&gt;

&lt;p&gt;Now you can start the server, therefore open the terminal in a folder where &lt;em&gt;docker-compose.yml&lt;/em&gt; is located and type:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After few minutes you should be able to enter the main page: &lt;a href="http://localhost:9000/" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2zy1t0dcgzt82ndaqb6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2zy1t0dcgzt82ndaqb6.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your SonarQube Server is not starting probably it’s related to ElasticSearch memory limitation. The solution to this problem can be found &lt;a href="https://stackoverflow.com/questions/51445846/elastic-search-max-virtual-memory-areas-vm-max-map-count-65530-is-too-low-inc" rel="noopener noreferrer"&gt;on the Stack Overflow&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Maven configuration
&lt;/h3&gt;

&lt;p&gt;Once we have SonarQube Server up and running let’s enable our projects to be able to generate reports that than can be consumed by SonarQube Server.&lt;/p&gt;

&lt;p&gt;Therefore we need to configure SonnarScanner, as the name may suggest, a tool for scanning projects. There are couple approaches for achieving this. All of them are described on &lt;a href="https://docs.sonarqube.org/latest/analysis/overview/" rel="noopener noreferrer"&gt;the official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I‘m picking a Maven approach because I’ve used it in my recent project. First step would be to set up global configuration of Maven, so go to your’s &lt;strong&gt;.m2&lt;/strong&gt; folder (it’s usually located in user root folder, more information about it you can find &lt;a href="https://www.baeldung.com/maven-local-repository" rel="noopener noreferrer"&gt;here&lt;/a&gt;) and add (or adjust if you already have) the &lt;em&gt;settings.xml&lt;/em&gt; file.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;settings&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;pluginGroups&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;pluginGroup&amp;gt;&lt;/span&gt;org.sonarsource.scanner.maven&lt;span class="nt"&gt;&amp;lt;/pluginGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/pluginGroups&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;sonar&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;activation&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;activeByDefault&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/activeByDefault&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/activation&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
     &lt;span class="nt"&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/settings&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Project’s POM config
&lt;/h3&gt;

&lt;p&gt;After setting up the global configuration of Maven you can go to your project.&lt;/p&gt;

&lt;p&gt;For demonstration purposes I’m using my recent project - &lt;a href="https://github.com/wkrzywiec/kanban-board/tree/master/kanban-app" rel="noopener noreferrer"&gt;&lt;strong&gt;Kanban-app&lt;/strong&gt;&lt;/a&gt;, which is a Java (Spring Boot) based REST application.&lt;/p&gt;

&lt;p&gt;The only thing that I would like to add here is a &lt;a href="https://www.baeldung.com/jacoco" rel="noopener noreferrer"&gt;JaCoCo Maven&lt;/a&gt; plugin that will generate a code coverage report which can be used by SonarQube (if don’t want to have such report you can skip this part, but I strongly recommend to have it).&lt;/p&gt;

&lt;p&gt;Therefore, open pom.xml file in your project and in the section build and then plugins add following lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--- some dependencies ---&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="c"&gt;&amp;lt;!--- different plugins ---&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.jacoco&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;jacoco-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;0.8.4&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;executions&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default-prepare-agent&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;prepare-agent&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;execution&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;default-report&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;phase&amp;gt;&lt;/span&gt;prepare-package&lt;span class="nt"&gt;&amp;lt;/phase&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;goals&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;goal&amp;gt;&lt;/span&gt;report&lt;span class="nt"&gt;&amp;lt;/goal&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/goals&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/execution&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/executions&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Running the analysis
&lt;/h3&gt;

&lt;p&gt;Everything should be set up now, so let’s run our first analysis!&lt;/p&gt;

&lt;p&gt;But before that, make sure that your SonarQube Server is running. Then go to your project and either in your IDE or in the terminal run following command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mvn clean verify sonar:sonar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And then enter the SonarQube — &lt;a href="http://localhost:9000/" rel="noopener noreferrer"&gt;http://localhost:9000&lt;/a&gt; and you probably see the number of projects that was analyzed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco2t4qh7pwf5xufiai3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fco2t4qh7pwf5xufiai3z.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To get more details click &lt;strong&gt;Projects&lt;/strong&gt; icon (at the top bar):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftv0dz21ueg8v1y1jzvjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftv0dz21ueg8v1y1jzvjq.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then click on the project name that you would like to check. In my case it’s &lt;em&gt;kanban-app&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In the new window you’ll see several issues that were discovered during the analysis. They can be &lt;em&gt;Bugs, Security Vulnerabilities, Code Smells, Duplications&lt;/em&gt; or &lt;em&gt;Code Coverage&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx097t7ocbdf0vf02zeow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx097t7ocbdf0vf02zeow.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By clicking on each one of them you should get more detailed report. For example, when I click on &lt;em&gt;Code Smells&lt;/em&gt; issues I’ve get following report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0j52tefnpocmfrrs4iv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0j52tefnpocmfrrs4iv.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can even dig deeper into it and find out in which line of code has the code smell.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4487k6fzksg3wx4cw8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4487k6fzksg3wx4cw8j.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These kind of reports are similar for every kind of an issue, except for &lt;em&gt;Code Coverage&lt;/em&gt;, to which I would like to move on.&lt;/p&gt;

&lt;p&gt;When you enter it you should get similar report:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftseynu34nv5vxlo1y59j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftseynu34nv5vxlo1y59j.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From my report you can see that the entire test coverage for my project is really low, so let’s fix it!&lt;/p&gt;

&lt;p&gt;First, let’s see if all classes needs to be tested. If you look closer you might see that the report includes some model and configuration classes for which writing unit test is rather pointless. Therefore we need to exclude them from the report.&lt;/p&gt;

&lt;p&gt;To do so, open &lt;strong&gt;pom.xml&lt;/strong&gt; file once again and in &lt;code&gt;&amp;lt;properties&amp;gt;&lt;/code&gt; section add &lt;code&gt;&amp;lt;sonar.exclusions&amp;gt;&lt;/code&gt; section in which you need to provide packages and/or classes that you want to exclude from the coverage report:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;properties&amp;gt;&lt;/span&gt;
       &lt;span class="c"&gt;&amp;lt;!--- Other properties ---&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;sonar.exclusions&amp;gt;&lt;/span&gt;
          **/model/**,**/config/**,**/KanbanApplication.java
       &lt;span class="nt"&gt;&amp;lt;/sonar.exclusions&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/properties&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;More information about the syntax could be found on &lt;a href="https://docs.sonarqube.org/latest/project-administration/narrowing-the-focus/" rel="noopener noreferrer"&gt;the official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Second thing to do is to write new unit tests that will cover untested parts of the code. To get to know which lines of code are not tested, move back to the report and pick a class that you would like to check.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uum69vp6pffz698ijql.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4uum69vp6pffz698ijql.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left, there are green and red blocks that indicates if this part of the code has unit tests.&lt;/p&gt;

&lt;p&gt;Once you know which methods are not tested yet, roll up your sleeves and start to write some test.&lt;/p&gt;

&lt;p&gt;After you’re finished with unit tests, rerun the analysis, with the same terminal command&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; mvn clean verify sonar:sonar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And refresh the SonarQube report page and you should get a new report.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F413xg196qaxfiufrncjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F413xg196qaxfiufrncjt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see my test coverage is still pretty low, but with SonarQube I’ll be able to fix it really quick!&lt;/p&gt;

&lt;p&gt;And that’s it! If you want to see a full code of my project you can go to my GitHub repositories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;with SonarQube Docker Compose&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec" rel="noopener noreferrer"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/develop-env" rel="noopener noreferrer"&gt;
        develop-env
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Development tools in Docker container
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;p&gt;with kanban-app&lt;br&gt;&lt;/p&gt;

&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec" rel="noopener noreferrer"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/kanban-board" rel="noopener noreferrer"&gt;
        kanban-board
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Single-click full-stack application (Postgres, Spring Boot &amp;amp; Angular) using Docker Compose
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dzone.com/articles/why-sonarqube-1" rel="noopener noreferrer"&gt;&lt;strong&gt;Why SonarQube: An Introduction to Static Code Analysis - DZone Performance&lt;/strong&gt; on dzone.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://hub.docker.com/_/sonarqube/" rel="noopener noreferrer"&gt;&lt;strong&gt;sonarqube - Docker Hub&lt;/strong&gt; on hub.docker.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.sonarqube.org/latest/analysis/scan/sonarscanner-for-maven/" rel="noopener noreferrer"&gt;&lt;strong&gt;SonarScanner for Maven&lt;/strong&gt; on docs.sonarqube.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>codequality</category>
      <category>sonar</category>
      <category>java</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to fetch REST API every 2 seconds with RxJava 2</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Wed, 17 Feb 2021 13:19:02 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/how-to-fetch-rest-api-every-2-seconds-with-rxjava-2-2b82</link>
      <guid>https://dev.to/wkrzywiec/how-to-fetch-rest-api-every-2-seconds-with-rxjava-2-2b82</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@whale"&gt;Matthew Smith&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;During my work on one of my Android application I’ve stomped on a problem where I needed to call some REST API every 10 seconds to check whether some data have changed or not. And with this blog post I would like to share with you how I’ve managed to achieve that.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;p&gt;First of all, I don’t want to show the Android code, because due to some code around it it may blur the solution. Instead for this demo I go with a simple command line app.&lt;/p&gt;

&lt;p&gt;Even though my application is very small I use couple tools that helps me develop the app and take care of some aspects that I don’t want to focus right now. So if you don’t feel comfortable with some parts of the code and you don’t know what it does, don’t worry. Here is a list of tools that I used with short explanation what it does.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Gradle&lt;/strong&gt; —it’s used to manage dependencies (external libraries). More info: &lt;a href="https://gradle.org/"&gt;https://gradle.org/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spring Boot&lt;/strong&gt; — I used it for dependency injection, where Spring, as a platform, take care of the lifecycle of the objects (creates and destroy them). More info: &lt;a href="https://spring.io/projects/spring-boot"&gt;https://spring.io/projects/spring-boot&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;RxJava 2 &amp;amp; Retrofit&lt;/strong&gt; — both libraries are used to perform API call and make it asynchronous. They are very popular in Android app development.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lombok&lt;/strong&gt; — it helps to skip writting boring parts of model class like, getters, setter, constructor and so on. You only need to add some annotation above the class name and it’ll work. More info: &lt;a href="https://medium.com/@wkrzywiec/project-lombok-how-to-make-your-model-class-simple-ad71319c35d5"&gt;Project Lombok&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1. Set up app structure
&lt;/h3&gt;

&lt;p&gt;So if you’re now ok with some of libraries that I use for this example we can go to the implementation. As it’s a &lt;em&gt;Spring Boot&lt;/em&gt; app go to &lt;a href="https://start.spring.io/"&gt;https://start.spring.io/&lt;/a&gt; where you can generate Spring app. To do that just pick a Gradle project (or Maven if you prefer) and customize artifacts (group, artifact). Then click &lt;strong&gt;Generate Project&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vLHnClHa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8bgdjmwiq0zmdaea15x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vLHnClHa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8bgdjmwiq0zmdaea15x.png" alt="spring initz" width="880" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After downloading it, unpack it and open with you favorite IDE (Eclipse, IntelliJ) — usually importing it as a Gradle project should work.&lt;/p&gt;

&lt;p&gt;Then go to the &lt;code&gt;RandomJokesApplication&lt;/code&gt; class and add create &lt;code&gt;Logger&lt;/code&gt; object and implement &lt;code&gt;CommandLineRunner&lt;/code&gt; interface, so it should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.slf4j.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.autoconfigure.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomJokesApplication&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CommandLineRunner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="no"&gt;LOG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RandomJokesApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SpringApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RandomJokesApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\n\n Hello World! \n\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;From now on, every “working” part of the code we will insert into the &lt;code&gt;run(…)&lt;/code&gt; method. Now if you run this app you should have some &lt;em&gt;Spring&lt;/em&gt; app info printed in the console as well as the &lt;em&gt;“Hello World!”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Great! Let’s move on to next part, but just before that, we need to add some dependencies to the &lt;em&gt;build.gradle&lt;/em&gt; file, so all necessary libraries will be downloaded automatically.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="c1"&gt;// some other default properties&lt;/span&gt;

&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'org.springframework.boot:spring-boot-starter'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'io.reactivex.rxjava2:rxjava:2.2.7'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.squareup.retrofit2:retrofit:2.5.0'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.squareup.retrofit2:converter-gson:2.5.0'&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.squareup.retrofit2:adapter-rxjava:2.5.0'&lt;/span&gt;

    &lt;span class="n"&gt;compileOnly&lt;/span&gt; &lt;span class="s1"&gt;'org.projectlombok:lombok:1.18.6'&lt;/span&gt;
    &lt;span class="n"&gt;annotationProcessor&lt;/span&gt; &lt;span class="s1"&gt;'org.projectlombok:lombok:1.18.6'&lt;/span&gt;
    &lt;span class="n"&gt;testImplementation&lt;/span&gt; &lt;span class="s1"&gt;'org.springframework.boot:spring-boot-starter-test'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For Lombok annotation processing you would also need to enable annotation processing in the settings of your IDE. Full instruction for each IDE can be found here: &lt;a href="https://projectlombok.org/setup/overview"&gt;https://projectlombok.org/setup/overview&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2. Create RxJava 2 Observable that prints something in a console each 2 seconds
&lt;/h3&gt;

&lt;p&gt;Now we can create an Observable that will print us something every 2 seconds.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.reactivex.Observable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;io.reactivex.schedulers.Schedulers&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// some other stuff mentioned before&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\n\n\t\t\t ---------  RandomJokesApplication is up and running --------- \n\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;


    &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;observeOn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newThread&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Tick"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\t\t\t ---------  RandomJokesApplication ends its work --------- "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With &lt;code&gt;interval()&lt;/code&gt; method we define a new Observable that will emits value each 2 seconds. &lt;code&gt;ObserveOn()&lt;/code&gt; tells us on which thread &lt;code&gt;subscribe()&lt;/code&gt; method will be running. And a final method is added to take care of the output of the emitted value (in our case it only prints the same String in the console).&lt;/p&gt;

&lt;p&gt;You may also see that a main Thread has been paused to sleep for 10 seconds. It’s because I want to run my program only for this amount of time during which the events will be triggered.&lt;/p&gt;

&lt;p&gt;When you run above code in the specified time frames in a console should be printed the same output — &lt;em&gt;“Tick”&lt;/em&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3. Add REST API service
&lt;/h3&gt;

&lt;p&gt;So as we handle a first part, let’s move on to the next one, which is mapping the API call to the Retrofit.&lt;/p&gt;

&lt;p&gt;A first step would be to create class models (data transfer objects) that reflects the structure of the API respond. For this demo I’m using &lt;a href="http://www.icndb.com/api/"&gt;The Internet Chuck Norris Database API&lt;/a&gt; that have specific URL for fetching, yes you guess so, Chuck Norris jokes! The URL is &lt;a href="https://api.icndb.com/jokes/random"&gt;https://api.icndb.com/jokes/random&lt;/a&gt; and the respond looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;550&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"joke"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Chuck Norris can speak Braille."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"categories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So to reflect above structure we need to create following classes (notice that I map only those fields that are really needed):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChuckNorrisJoke&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ChuckNorrisJokeValue&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ChuckNorrisJokeValue&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;joke&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;em&gt;&lt;a class="mentioned-user" href="https://dev.to/data"&gt;@data&lt;/a&gt;&lt;/em&gt; annotation is a Lombok annotation, and creates for us getters, setters, &lt;code&gt;toString()&lt;/code&gt;, &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt; and no arguments constructor.&lt;/p&gt;

&lt;p&gt;Then we need to create an interface that will map URL resource path to the method. The method should return the Observable and looks like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;retrofit2.http.GET&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;rx.Observable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ChuckNorrisJokesApi&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/jokes/random"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ChuckNorrisJoke&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;randomJoke&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You may notice that in @GET annotation only partial URL is provided. The remaining base needs to be provided in a service class, which we will be our utility class to fetch jokes from the Chuck Norris API.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.stereotype.Service&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;retrofit2.Retrofit&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;retrofit2.adapter.rxjava.RxJavaCallAdapterFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;retrofit2.converter.gson.GsonConverterFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;rx.Observable&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;JokesService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ChuckNorrisJokesApi&lt;/span&gt; &lt;span class="n"&gt;chuckNorrisJokesApi&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nc"&gt;JokesService&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;Retrofit&lt;/span&gt; &lt;span class="n"&gt;retrofit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;baseUrl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://api.icndb.com/"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addConverterFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GsonConverterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addCallAdapterFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RxJavaCallAdapterFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;chuckNorrisJokesApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retrofit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ChuckNorrisJokesApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getRandomChuckNorrisJoke&lt;/span&gt;&lt;span class="o"&gt;(){&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;chuckNorrisJokesApi&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;randomJoke&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;respond&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;respond&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getValue&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getJoke&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At first glance the &lt;code&gt;JokesService&lt;/code&gt; class may not be straight forward, but basically it only sets up the &lt;code&gt;ChuckNorrisApi&lt;/code&gt; object and uses in a single method that is responsible for retrieving a text of the joke wrapped in a Observable.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;@Service&lt;/code&gt; annotation comes with Spring and tells Dependency Injection Container that it should take care of this object life cycle.&lt;/p&gt;

&lt;p&gt;Finally we can inject our service (using @Autowired annotation) into the main class — &lt;code&gt;RandomJokesApplication&lt;/code&gt; and then use it in &lt;code&gt;run(…)&lt;/code&gt; method. For now just to print a single joke.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...beginging of the class&lt;/span&gt;

&lt;span class="nd"&gt;@Autowired&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;JokesService&lt;/span&gt; &lt;span class="n"&gt;jokesService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ...other middle stuff...&lt;/span&gt;

&lt;span class="nd"&gt;@Override&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\n\n\t\t\t ---------  PeriodicQuotesApplication is up and running --------- \n\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;jokesService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRandomChuckNorrisJoke&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\n\t\t\t {} \n\n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// following code...&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you run above code you should get a single Chuck Norris joke and which means that we have only one thing left ahead — combining it with periodically triggered &lt;code&gt;Observable&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 4. Combing all together
&lt;/h3&gt;

&lt;p&gt;To do that we could move a part of the code that is printing the joke to the &lt;code&gt;subscribe()&lt;/code&gt; method of Observable that is printing something in each 2 seconds. But I would like to try a little different approach:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@SpringBootApplication&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RandomJokesApplication&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;CommandLineRunner&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// some dependencies...&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\n\n\t\t\t ---------  RandomJokesApplication is up and running --------- \n\n"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;


        &lt;span class="nc"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;SECONDS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;io&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;observeOn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Schedulers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newThread&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tick&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;jokesService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRandomChuckNorrisJoke&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doOnError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;retry&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jokeObservable&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;jokeObservable&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;joke&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\n\t\t\t {} \n\n"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;joke&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;

        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

            &lt;span class="no"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\n\n\t\t\t ---------  RandomJokesApplication ends its work --------- "&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Firs of all, I’ve added a &lt;code&gt;map()&lt;/code&gt; step in which joke text is extracted, so the &lt;code&gt;subscribe()&lt;/code&gt; method is much more cleaner. Also &lt;code&gt;doOnError()&lt;/code&gt; and &lt;code&gt;retry()&lt;/code&gt; steps were added to take care of error and make sure than even some of them occurs Observable will still fire the event.&lt;/p&gt;

&lt;p&gt;If you run the application, you should get similar output:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WUZLLF7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wspr4jre6wkqhvqi1xb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WUZLLF7L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4wspr4jre6wkqhvqi1xb.gif" alt="Alt Text" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that’s it! 🤓&lt;/p&gt;

&lt;p&gt;The entire code for this app could be found here (if you dig into commits history you can also see that a code was written similar to described story) :&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/Random-Jokes"&gt;
        Random-Jokes
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Testing RxJava 2 periodic capabilities
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://blog.freeside.co/2015/01/29/simple-background-polling-with-rxjava/"&gt;&lt;strong&gt;Ad-Hockery - Simple background polling with RxJava&lt;/strong&gt; on blog.freeside.co&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.journaldev.com/23007/android-retrofit-call-every-x-seconds"&gt;&lt;strong&gt;Android Retrofit Call Every X Seconds&lt;/strong&gt; on journaldev.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>reactive</category>
      <category>rest</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What was added to Java 8? Optional&lt;T&gt; class</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Sun, 14 Feb 2021 09:43:10 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/what-was-added-to-java-8-optional-t-class-58c</link>
      <guid>https://dev.to/wkrzywiec/what-was-added-to-java-8-optional-t-class-58c</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@isaacbenhesed"&gt;Isaac Benhesed&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;As a Java developer you’ve probably stomped on NullPointerException many times and usually handling this kind of error takes some time. Luckily thanks to new Optional class it’s much easier now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a three-part series on Java 8 new features, other blog posts can be found here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/wkrzywiec/what-was-added-to-java-8-lambda-expressions-459j"&gt;Lambda expression&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/wkrzywiec/what-was-added-to-java-8-streams-35kl"&gt;Streams&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional class (current)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This class works as a class container (or wrapper) which may or may not hold null value. Before introducing Optional class developers to make sure that objects references aren’t null were forced to check it before calling any method (if there were a great chance for it, of course). To illustrate this, see below example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;pizzaRecipeText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookBook&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;Reciepes&lt;/span&gt; &lt;span class="n"&gt;reiepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cookBook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRecipes&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepes&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Reciepe&lt;/span&gt; &lt;span class="n"&gt;pizzaReciepe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pizza"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pizzaReciepe&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;pizzaRecipeText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pizzaReciepe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getText&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if it’s a simple code it requires lots of boilerplate checking of nulls, which is not either fun to write or read. Also if we forget to check it, it becomes a potential &lt;em&gt;“dangerous”&lt;/em&gt; part of a code.&lt;/p&gt;

&lt;p&gt;So instead calling directly a class method that may or may not return null we can wrap it in Optional class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CookBook&lt;/span&gt; &lt;span class="n"&gt;cookBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CookBook&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookBook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="n"&gt;optReciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepes&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pizza"&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CookBook&lt;/code&gt; class doesn’t have &lt;code&gt;Reciepes&lt;/code&gt; class unless at least one of them was added to it. Therefore, in above example &lt;code&gt;cookBook.getReciepes()&lt;/code&gt; method will result with a null, which then would result in our favourite exception while getting any reciepe.&lt;/p&gt;

&lt;p&gt;Instead we can wrap it with &lt;strong&gt;Optional&lt;/strong&gt; class and use &lt;strong&gt;ifPresent&lt;/strong&gt; method that checks it for us and if it’s available it’ll proceed. In above example &lt;code&gt;optReciepes&lt;/code&gt; reference holds null so the 4th line of the code won’t result in printing pizza reciepe, but it also won’t result in &lt;strong&gt;NullPointerException&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;What if we won’t to pass default value instead? We can use &lt;strong&gt;orElse&lt;/strong&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CookBook&lt;/span&gt; &lt;span class="n"&gt;cookBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CookBook&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookBook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;Reciepes&lt;/span&gt; &lt;span class="n"&gt;reciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;Reciepe&lt;/span&gt; &lt;span class="n"&gt;pizzaReciepe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pizza"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Reciepe&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As it was earlier &lt;code&gt;optReciepes&lt;/code&gt; holds null, but by invoking &lt;strong&gt;orElse&lt;/strong&gt; method we create a default instance of a class. A default instance is passed as a parameter of this method.&lt;/p&gt;

&lt;p&gt;Another approach for same situation would be throwing different exception, instead of returning a default value. Moving forward with an example, the &lt;code&gt;Reciepes&lt;/code&gt; class does not have any reciepe yet, so when we call &lt;code&gt;reciepes.getReciepe("Pizza")&lt;/code&gt; it should throw &lt;code&gt;ReciepeNotFoundException&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CookBook&lt;/span&gt; &lt;span class="n"&gt;cookBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CookBook&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookBook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;Reciepes&lt;/span&gt; &lt;span class="n"&gt;reciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;Reciepe&lt;/span&gt; &lt;span class="n"&gt;pizzaReciepe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepe&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Pizza"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;ReciepeNotFoundException:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except for mentioned above methods to extract the data from the Optional objects we can make it more clear by using &lt;strong&gt;map&lt;/strong&gt; operator. Like in below example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;CookBook&lt;/span&gt; &lt;span class="n"&gt;cookBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CookBook&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cookBook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getReciepes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;reciepesCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Reciepes:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getTotalCount&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Together with &lt;code&gt;map&lt;/code&gt; function we can use a &lt;code&gt;filter&lt;/code&gt; that will automatically reject the output of mapping if it won’t meet certain conditions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CookBook&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optCookBook&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofNullable&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kitchen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBook&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Italian"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Reciepes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;optReciepes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;optCookBook&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CookBook:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getReciepes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepe&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"Pizza"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reciepe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTitle&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To sum up Optional class allows us to handle NullPointerException in more graceful way.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html"&gt;&lt;strong&gt;Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!&lt;/strong&gt; on oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/java-optional"&gt;&lt;strong&gt;Guide To Java 8 Optional&lt;/strong&gt; on baeldung.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>optional</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What was added to Java 8? Streams</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Wed, 03 Feb 2021 14:44:16 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/what-was-added-to-java-8-streams-35kl</link>
      <guid>https://dev.to/wkrzywiec/what-was-added-to-java-8-streams-35kl</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@samthewam24"&gt;Samuel Sianipar&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Many programming tasks can be described as data processing i.e. we’ve got a collection of values which we want modify, like filter, transform or group. Until Java 8 this task was really painful (required multiple loops) and was not such efficient. Luckily there is new concept — Streams.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a three-part series on Java 8 new features, other blog posts can be found here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://dev.to/wkrzywiec/what-was-added-to-java-8-lambda-expressions-459j"&gt;Lambda expression&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Streams (current)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Optional class (soon)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Java 8 introduced new pipeline-mechanism for data processing. It’s usually compared to pipeline or assembly line, because on start as an argument we provide a data collection and then we pass it thru the operations that will modify it and get another output.&lt;/p&gt;

&lt;p&gt;To give you a picture of what stream capabilities are see below example. It compares the same task that was made with “standard” and stream based approach.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;shop&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTshirtCollection&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;selectedTshirts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// without Streams&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt; &lt;span class="nl"&gt;tshirt:&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RED"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSize&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"M"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
      &lt;span class="n"&gt;selectedTshirts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tShirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toUpperCase&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;//with Streams&lt;/span&gt;
&lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RED"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSize&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"M"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toUpperCase&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if I haven’t introduce the syntax yet we can see major benefits already. First of all, the code is much simplier and cleaner. And even if we don’t know all the operators yet they’re easy to understand.&lt;/p&gt;

&lt;p&gt;To cover all these tasks Oracle team introduced new package &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html"&gt;java.util.stream&lt;/a&gt; that contains &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html"&gt;Stream&lt;/a&gt; class. In above example method &lt;em&gt;stream()&lt;/em&gt; was called on collection, which results in a &lt;em&gt;Stream&lt;/em&gt; — it means that a stream of t-shirt objects has been created from their list 👕.&lt;/p&gt;

&lt;p&gt;All methods related to Streams can be categorized into three groups:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Stream producing methods,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stream operating methods (intermediate methods),&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Stream terminal methods.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stream producing methods
&lt;/h3&gt;

&lt;p&gt;Before we can operate on the objects stream first we need to create it. With a Java update Oracle has added a new method to &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html"&gt;Collection&lt;/a&gt; interface with a name &lt;strong&gt;&lt;em&gt;stream()&lt;/em&gt;&lt;/strong&gt; which converts object that implements this interface (like Lists, Maps or Sets) into the Stream.&lt;/p&gt;

&lt;p&gt;The above statement indicates that only objects, like &lt;em&gt;ArrayList&lt;/em&gt;, or &lt;em&gt;HashSet&lt;/em&gt; can be converted into Stream.&lt;/p&gt;

&lt;p&gt;We could also want to generate it from an array, using static method of the Arrays class — &lt;strong&gt;&lt;em&gt;Arrays.stream(T[] array)&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;arrayOfExpressions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hi everyone!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Good Morning!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"How u doin'?"&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;streamfromArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arrayOfExpressions&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we don’t have a list of objects we can create an infinite stream of integers that will start from 2 and will be increment by 3. To do so we can use static method of the &lt;em&gt;Stream&lt;/em&gt; class — &lt;strong&gt;&lt;em&gt;iterate()&lt;/em&gt;&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;infiniteStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;
                                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;&lt;em&gt;limit()&lt;/em&gt;&lt;/strong&gt; step is necessary to end the infinite loop and it tells that we want to obtain a stream of 10 elements.&lt;/p&gt;

&lt;p&gt;Another way to create a stream is to use static &lt;strong&gt;&lt;em&gt;generate()&lt;/em&gt;&lt;/strong&gt; method of &lt;em&gt;Stream&lt;/em&gt; class. Below code will result in a 10-elements stream where each element is a &lt;em&gt;“text”&lt;/em&gt; String.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;
                          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                          &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Stream operating methods (intermediate methods)
&lt;/h3&gt;

&lt;p&gt;Once a stream is created we can perform multiple manipulation on it’s elements. The key concept for this is that we can create several operations that are chained together so they’re executed one after the another. It’s possible, because each operation returns a new instance of &lt;em&gt;Stream&lt;/em&gt; class.&lt;/p&gt;

&lt;p&gt;Here is the list of some (but not all) operations that can be performed on the Stream:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;filter()&lt;/em&gt;&lt;/strong&gt;— works similarly to WHERE clause in the SQL, it filters elements that match condition.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RED"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;map()&lt;/em&gt;&lt;/strong&gt; — it’s used when we want to transform each element in a Stream. For example, when we want to extract a value from a field in an object (&lt;em&gt;name&lt;/em&gt; from the Tshirt class) or convert one value to another (kilograms to pounds).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirtNameStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Tshirt:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Double&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirtPoundWeightStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Tshirt:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getKilogramWeight&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;kg&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;kg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mf"&gt;2.205&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;flatMap()&lt;/em&gt;&lt;/strong&gt; — this operation is very similar to &lt;em&gt;map()&lt;/em&gt;, but it also perform “flatten” task. It’s required when a body of &lt;em&gt;map()&lt;/em&gt; operation returns a list or an array of values (not a single value as it was previously). If we use &lt;em&gt;map()&lt;/em&gt; operation to perform such task we would receive a Stream of the Streams, each for an each element in a result list. But if we use &lt;em&gt;flatMap()&lt;/em&gt; all of these Streams will be combined into single Stream.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;arrayOfExpressions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"Hello World!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Hi everyone!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Good Morning!"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"How u doin'?"&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;streamOfStreamsOfWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arrayOfExpressions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\s+"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;streamOfWords&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;arrayOfExpressions&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flatMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;split&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\s+"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;sorted()&lt;/em&gt;&lt;/strong&gt; — works similar to ORDER BY clause in the SQL, it sorts elements ascending/descending. If we don’t provide any argument to this method, all records will be natural ordered ascending. But if we input &lt;strong&gt;&lt;em&gt;Comparator.reverseOrder()&lt;/em&gt;&lt;/strong&gt; as an argument it will be order descending.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;infiniteStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Stream&lt;/span&gt;
                                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;iterate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ascOrderedStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infiniteStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sorted&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Integer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dscOrderedStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;infiniteStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sorted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;reverseOrder&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It might do a trick, when we need to sort Strings or digits, but if we won’t to order complex objects we need to create a Comperator object that will indicate which object field should be used for sorting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ascOrderedTshirts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sorted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;comparing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Tshirt:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getSize&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="nc"&gt;Stream&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dscOrderedTshirts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sorted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;comparing&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Tshirt:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getSize&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;reversed&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;distinct()&lt;/em&gt;&lt;/strong&gt; — returns a Stream without any duplicate elements, all elements are unique.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stream terminal methods
&lt;/h3&gt;

&lt;p&gt;After performing all these transformation operation on a Stream we want to see a result of it. Stream class is just a wrapper so to get value from it we need to perform one of terminating actions listed below.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;collect()&lt;/em&gt;&lt;/strong&gt; —Stream can be compared to a list of elements, so this operation is used to convert Stream to a list (or any other collection instance). By passing an argument to it (it’s &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html"&gt;Collector&lt;/a&gt; object) we can specify what will be the result.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if we use static method &lt;strong&gt;&lt;em&gt;Collector.toList()&lt;/em&gt;&lt;/strong&gt; to get a list of objects, or using &lt;strong&gt;&lt;em&gt;Collector.toSet()&lt;/em&gt;&lt;/strong&gt; will result in Set object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redTshirtsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RED"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mediumTshirtSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSize&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"M"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toSet&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apart from build-in Collector method we can use more sophisticated approach. We can use &lt;strong&gt;&lt;em&gt;Collector.toColleaction()&lt;/em&gt;&lt;/strong&gt; method we can specify a Collection object type (e.g. HashSet).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tshirt&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mediumTshirtSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSize&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"M"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toCollection&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;HashSet:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it’s not everything that we can do with this method. For example, we can group (like in SQL) results to get their count, max value by the specific field. Or we can sum all values from specific fields. These an other examples can be found in the &lt;a href="https://www.oracle.com/technetwork/articles/java/architect-streams-pt2-2227132.html"&gt;Oracle article&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;toArray()&lt;/em&gt;&lt;/strong&gt; — works similar to previous one, but here it results in an array of objects, not a Collection.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;em&gt;forEach()&lt;/em&gt;&lt;/strong&gt; — with this method we don’t return any object (return type is void), but we can perform some action on each element that is in a Stream. For example we can print names in the console or perform an action on each of element.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;tshirtCollection&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tshirt&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tshirt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getColor&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"RED"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;Tshirt:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
             &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;println&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it. With this article I’ve only touched the surface of Java Stream but it’s a good point to start with. If you want to know more check the official documentation and play around it to know all the capabilities that it can offer.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/technetwork/articles/java/ma14-java-se-8-streams-2177646.html"&gt;&lt;strong&gt;Processing Data with Java SE 8 Streams, Part 1&lt;/strong&gt; on oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/technetwork/articles/java/architect-streams-pt2-2227132.html"&gt;&lt;strong&gt;Part 2: Processing Data with Java SE 8 Streams&lt;/strong&gt; on oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.baeldung.com/java-8-streams"&gt;&lt;strong&gt;The Java 8 Stream API Tutorial | Baeldung&lt;/strong&gt; on baeldung.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.deadcoderising.com/2015-05-19-java-8-replace-traditional-for-loops-with-intstreams/"&gt;&lt;strong&gt;Java 8: Replace traditional for loops with IntStreams&lt;/strong&gt; on deadcoderising.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>beginners</category>
      <category>functional</category>
      <category>java8</category>
    </item>
    <item>
      <title>What was added to Java 8? Lambda expressions</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Wed, 27 Jan 2021 10:04:37 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/what-was-added-to-java-8-lambda-expressions-459j</link>
      <guid>https://dev.to/wkrzywiec/what-was-added-to-java-8-lambda-expressions-459j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@iswanto"&gt;Iswanto Arif&lt;/a&gt; on &lt;a href="https://unsplash.com"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;With this article I start a short series that will explain what features were added to Java 8 update. Today I’ll focus on the major buzz of this release — lambda expression (a.k.a. lambdas).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These series will be divided into three parts (links will be updated once each blog post will be published):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Part 1&lt;/strong&gt;. &lt;em&gt;Lambda expression (this one)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Part 2.&lt;/strong&gt; &lt;em&gt;Streams (soon)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Part 3.&lt;/strong&gt; &lt;em&gt;Optional (soon)&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Lambda expression
&lt;/h3&gt;

&lt;p&gt;When you start to learn Java and you have already passed command line examples you probably wants to create a desktop application. And for this you probably use &lt;a href="https://en.wikipedia.org/wiki/JavaFX"&gt;JavaFX&lt;/a&gt; library (at least it was my case).&lt;/p&gt;

&lt;p&gt;In most of JavaFX applications we need to handle events that can be triggered by the users. For example, when they press a button it’ll create an Event object that need to be handled.&lt;/p&gt;

&lt;p&gt;Therefore we need to assign an action which will be triggered once user click the button. For this task we usually declare &lt;a href="https://www.geeksforgeeks.org/anonymous-inner-class-java/"&gt;anonymous inner class&lt;/a&gt;, which has only one method that performs necessary actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setOnAction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventHandler&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="nd"&gt;@Override&lt;/span&gt;
       &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ActionEvent&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button clicked"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
       &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if above code is pretty straightforward it requires lots of writing and when code starts to grow it’s also becomes hard to ready. Luckily, thanks to lambda expression we can write it in a more compact way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setOnAction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button clicked"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whoa! That’s short, but what’s going on there?&lt;/p&gt;

&lt;p&gt;Lambda expression syntax is made of three parts. First one are brackets, &lt;code&gt;(e)&lt;/code&gt; , that contains (or not) parameters of the abstract method of an &lt;em&gt;anonymous inner class&lt;/em&gt;. &lt;strong&gt;It’s really important to remember that lamdas can be used only when a single abstract method&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our case &lt;em&gt;ActionEvent&lt;/em&gt; object is represented by &lt;code&gt;e&lt;/code&gt; reference. If the method doesn’t have any parameter we can use simple &lt;code&gt;()&lt;/code&gt; instead, like for &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html"&gt;&lt;em&gt;Runnable&lt;/em&gt;&lt;/a&gt; interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Runnable&lt;/span&gt; &lt;span class="n"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I'm in outside main thread!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally method can have more than one arguments, like &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html"&gt;Comperator&lt;/a&gt; interface method &lt;code&gt;compare&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Comparator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;userComperator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;compareTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;second&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In above example we could also don’t include argument types (User), but for clarity reasons it good to be added.&lt;/p&gt;

&lt;p&gt;Next, after the arguments there is a newly introduce right arrow &lt;code&gt;-&amp;gt;&lt;/code&gt; operator.&lt;/p&gt;

&lt;p&gt;And finally there is a body of the implemented method. Usually it’s a one line of code, but if we require more we can surround it with &lt;code&gt;{}&lt;/code&gt; brackets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setOnAction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button clicked"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Clicked"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a method should return a value we can use &lt;code&gt;return&lt;/code&gt; statement as it’s in regular methods&lt;/p&gt;

&lt;h3&gt;
  
  
  Method reference (::)
&lt;/h3&gt;

&lt;p&gt;Another topic that is tightly related and was introduced together with lambda expression is referencing the method. In short, with new operator &lt;code&gt;::&lt;/code&gt; we can assign method to a reference, just like the object or primitive type. This approach allow us to extract the method from the object and pass it in another place without executing them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;objectInstance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;IntSupplier&lt;/span&gt; &lt;span class="n"&gt;equalsMethodOnObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nl"&gt;objectInstance:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;equalsMethodOnObject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAsInt&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Above we assign a &lt;em&gt;hashCode&lt;/em&gt; method to a reference &lt;a href="https://docs.oracle.com/javase/8/docs/api/java/util/function/IntSupplier.html"&gt;IntSupplier&lt;/a&gt;. Then it can be passed wherever we want in the code.&lt;/p&gt;

&lt;p&gt;With method reference we can also assign static methods (without creating instance of the class) or constructor.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oracle.com/technetwork/articles/java/architect-lambdas-part1-2080972.html"&gt;&lt;strong&gt;Java 8: Lambdas, Part 1&lt;/strong&gt; on oracle.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://tutorials.jenkov.com/java/lambda-expressions.html"&gt;&lt;strong&gt;Java Lambda Expressions&lt;/strong&gt; on tutorials.jenkov.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codementor.io/eh3rrera/using-java-8-method-reference-du10866vx"&gt;&lt;strong&gt;Java 8 Method Reference: How to Use it&lt;/strong&gt; on codementor.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.makery.ch/blog/javafx-8-event-handling-examples/"&gt;&lt;strong&gt;JavaFX 8 Event Handling Examples&lt;/strong&gt; on code.makery.ch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>java</category>
      <category>beginners</category>
      <category>functional</category>
    </item>
    <item>
      <title>How to declaratively run Helm charts using helmfile</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Thu, 21 Jan 2021 09:33:56 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/how-to-declaratively-run-helm-charts-using-helmfile-4ld8</link>
      <guid>https://dev.to/wkrzywiec/how-to-declaratively-run-helm-charts-using-helmfile-4ld8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@ikukevk" rel="noopener noreferrer"&gt;Kevin Ku&lt;/a&gt; on &lt;a href="https://unsplash.com" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Have you ever wonder how to achieve fully Infrastructure as a Code approach for deployment of your applications on Kubernetes cluster? No matter if you’ve already achieve it or just started to looking for the best tool, helmfile might be the answer for you. In this blog post I’ll present how you can set up your first helmfile.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This post is a third, and the last, from my series on Kubernetes. In each one of them I present how to deploy applications on a Kubernetes cluster using one of the approaches:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;using &lt;strong&gt;kubectl&lt;/strong&gt; — &lt;a href="https://dev.to/wkrzywiec/deployment-of-multiple-apps-on-kubernetes-cluster-walkthrough-32km"&gt;Deployment of multiple apps on Kubernetes cluster — Walkthrough&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;using &lt;strong&gt;Helm&lt;/strong&gt; — &lt;a href="https://dev.to/wkrzywiec/how-to-deploy-application-on-kubernetes-with-helm-8p"&gt;How to deploy application on Kubernetes with Helm&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;using &lt;strong&gt;helmfile&lt;/strong&gt; — this one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t read first two I would recommend to do that before diving into this one. But it’s not the must, I’ll briefly introduce you to an environment which will be built.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft49u4lvf2bsfn3xbihq4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ft49u4lvf2bsfn3xbihq4.png" alt="arch"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above picture presents the target solution. Inside the cluster there will be deployed frontend (&lt;em&gt;kanban-ui&lt;/em&gt;) and backend (&lt;em&gt;kanban-app&lt;/em&gt;). To persist data postgres database will be used. A source code for both microservices can be found in my GitHub repository — &lt;a href="https://github.com/wkrzywiec/kanban-board" rel="noopener noreferrer"&gt;kanban-board&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally I’ve added an &lt;em&gt;adminer&lt;/em&gt; application, which is a GUI client for getting inside the database.&lt;/p&gt;

&lt;p&gt;The above picture shows not only applications that will be deployed on &lt;em&gt;Kubernetes&lt;/em&gt; cluster, but also objects that are required to run them, like &lt;em&gt;Deployments&lt;/em&gt; or &lt;em&gt;Services&lt;/em&gt;. With classic, &lt;em&gt;kubectl&lt;/em&gt; approach, we were forced to create definition files for each one of them. Even if in most cases, for example in case of &lt;em&gt;ClusterIPs&lt;/em&gt;, these objects haven’t differ that much between applications.&lt;/p&gt;

&lt;p&gt;Definitely this approach doesn’t scale in microservice world, where usually there is a need to deploy dozens or even hundreds of applications. And if for each one of them we would be require to create couple YAML files it would really quick become a nightmare. But this problem might be solved with &lt;em&gt;Kubernetes&lt;/em&gt; templating engine — &lt;em&gt;&lt;a href="https://helm.sh" rel="noopener noreferrer"&gt;Helm&lt;/a&gt;&lt;/em&gt;. We can define a generic &lt;em&gt;Helm&lt;/em&gt; chart (template) and reused it across multiple applications only by injecting specific values into the template.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is it problem solved?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To some extend yes, but here is a drawback of using &lt;em&gt;Helm&lt;/em&gt;. In order to install or update any chart in a cluster you need to run a specific imperative command. In other words, in order to change state of a cluster you need to run a command that is specific for a given deployment.&lt;/p&gt;

&lt;p&gt;In Java world we’re using &lt;a href="https://maven.apache.org" rel="noopener noreferrer"&gt;Maven&lt;/a&gt; or for JavaScript we use &lt;em&gt;&lt;a href="https://www.npmjs.com" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/em&gt;. Both of these tools allows to run the same command across multiple projects to perform the same action like running tests— &lt;code&gt;mvn test&lt;/code&gt;. Helm currently doesn’t support this feature, we’re not able to install, update or rollback all applications from an entire cluster with a single command.&lt;/p&gt;

&lt;p&gt;But there is a &lt;a href="https://github.com/roboll/helmfile" rel="noopener noreferrer"&gt;helmfile&lt;/a&gt;, which might be a remedy.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Helmfile&lt;/em&gt; allows to declare a definition of an entire &lt;em&gt;Kubernetes&lt;/em&gt; cluster in a single YAML file, it bundles multiple &lt;em&gt;Helm&lt;/em&gt; releases (installation of &lt;em&gt;Helm&lt;/em&gt; charts) and allows to tune a specification of each release depending on a type of environment (develop, test, production) on which you might want to deploy your applications.&lt;/p&gt;

&lt;p&gt;The best approach would be to show how it works on an example. Before that we need to set up a working environment. If you want to follow my steps, you would need to install and configure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/install/" rel="noopener noreferrer"&gt;Docker,&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;locally installed Kubernetes cluster — &lt;a href="https://kubernetes.io/docs/tasks/tools/install-minikube/" rel="noopener noreferrer"&gt;minikube&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kubernetes command line tool — &lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt;kubectl,&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;Helm (v3)&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/roboll/helmfile#installation" rel="noopener noreferrer"&gt;helmfile&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When everything is installed you can start up the &lt;em&gt;minikube&lt;/em&gt; cluster and enable ingress addon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; minikube start 
😄  minikube v1.8.1 on Ubuntu 18.04
✨  Automatically selected the docker driver
🔥  Creating Kubernetes &lt;span class="k"&gt;in &lt;/span&gt;docker container with &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CPUs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;8 available&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2200MB &lt;span class="o"&gt;(&lt;/span&gt;7826MB available&lt;span class="o"&gt;)&lt;/span&gt; ...
🐳  Preparing Kubernetes v1.17.3 on Docker 19.03.2 ...
▪ kubeadm.pod-network-cidr&lt;span class="o"&gt;=&lt;/span&gt;10.244.0.0/16
❌  Unable to load cached images: loading cached images: &lt;span class="nb"&gt;stat&lt;/span&gt; /home/wojtek/.minikube/cache/images/k8s.gcr.io/kube-proxy_v1.17.3: no such file or directory
🚀  Launching Kubernetes ...
🌟  Enabling addons: default-storageclass, storage-provisioner
⌛  Waiting &lt;span class="k"&gt;for &lt;/span&gt;cluster to come online ...
🏄  Done! kubectl is now configured to use &lt;span class="s2"&gt;"minikube"&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;ingress
🌟  The &lt;span class="s1"&gt;'ingress'&lt;/span&gt; addon is enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then you’ll need to edit you &lt;strong&gt;hosts&lt;/strong&gt; file. Its location varies on OS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://manpages.ubuntu.com/manpages/trusty/man5/hosts.5.html" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.groovypost.com/howto/edit-hosts-file-windows-10/" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.imore.com/how-edit-your-macs-hosts-file-and-why-you-would-want#page1" rel="noopener noreferrer"&gt;MacOS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you find it add following lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;172.17.0.2  adminer.k8s.com
172.17.0.2  kanban.k8s.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To make sure that this config is correct you’ll need to check if an IP address of &lt;em&gt;minikube&lt;/em&gt; cluster on your machine is the same as it’s above. In order to do that run the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; minikube ip
172.17.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A preparations are done, let’s do some &lt;em&gt;helmfile&lt;/em&gt; magic. In previous blog post — &lt;em&gt;&lt;a href="https://dev.to/wkrzywiec/how-to-deploy-application-on-kubernetes-with-helm-8p"&gt;How to deploy application on Kubernetes with Helm&lt;/a&gt;&lt;/em&gt; — I’ve described how to create Helm charts for Kanban Board project. Here we will reuse them. Therefore copy-paste all the files from here — &lt;a href="https://github.com/wkrzywiec/k8s-helm-helmfile/tree/master/helm" rel="noopener noreferrer"&gt;k8s-helm-helmfile/helm&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All folders — &lt;code&gt;app&lt;/code&gt; , &lt;code&gt;ingress&lt;/code&gt; , &lt;code&gt;postgres&lt;/code&gt; — put inside a new one called charts and all YAML files put inside a new folder called &lt;code&gt;values&lt;/code&gt;. Then add a new &lt;strong&gt;helmfile.yaml&lt;/strong&gt; file to a root directory, so the resulting structure will looks as follows:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvjub00rj7k59bsk7tzu3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvjub00rj7k59bsk7tzu3.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me briefly explain what each of these folders means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;./charts&lt;/code&gt; — here are all three Helms charts that are templates for each release — &lt;code&gt;app&lt;/code&gt; (for &lt;em&gt;adminer&lt;/em&gt;, &lt;em&gt;kanban-ui&lt;/em&gt;, &lt;em&gt;kanban-app&lt;/em&gt;), &lt;code&gt;postgres&lt;/code&gt; and &lt;code&gt;ingress&lt;/code&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;helmfile.yaml&lt;/code&gt; — here will be a configuration for a &lt;em&gt;helmfile&lt;/em&gt;, currently blank,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;./values&lt;/code&gt; — a folder with a values that are specific for each application which will be deployed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before moving to filling a &lt;strong&gt;helmfile.yaml&lt;/strong&gt; I want to adjust one thing. In a previous, native &lt;em&gt;Helm&lt;/em&gt;, solution there is an &lt;code&gt;ingress&lt;/code&gt; chart, which has a dependency to &lt;a href="https://hub.helm.sh/charts/stable/nginx-ingress" rel="noopener noreferrer"&gt;nginx-ingress Helm chart available on Helm Hub&lt;/a&gt;. Before installing it on a cluster we needed to download it (will be saved in &lt;code&gt;.charts/ingress/charts&lt;/code&gt; folder) using separate helm command.&lt;/p&gt;

&lt;p&gt;If we use &lt;em&gt;helmfile&lt;/em&gt;, the last step would not be needed, because it will figure out and download all required dependencies inside each chart. So we could keep &lt;code&gt;ingress&lt;/code&gt; chart as it is, but I would like to have a different approach this time. I would like to treat &lt;em&gt;nginx-ingress&lt;/em&gt; chart as a separate &lt;em&gt;Helm&lt;/em&gt; release, apart from the &lt;em&gt;Ingress Controller&lt;/em&gt; configuration for routing.&lt;/p&gt;

&lt;p&gt;Therefore remove following lines from the &lt;code&gt;./charts/ingress/Chart.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-ingress&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.36.0&lt;/span&gt;
    &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes-charts.storage.googleapis.com/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Also you can delete the &lt;code&gt;Chart.lock&lt;/code&gt; file entirely.&lt;/p&gt;

&lt;p&gt;After those changes ingress &lt;em&gt;Helm&lt;/em&gt; chart is responsible only for defining *Ingress Controller. The required Ingress backend service will be introduced with Helm release.&lt;/p&gt;

&lt;p&gt;Now, finally we can move to arranging a &lt;strong&gt;helmfile.yaml&lt;/strong&gt; .&lt;/p&gt;

&lt;p&gt;First we need to indicate from which &lt;em&gt;Helm&lt;/em&gt; repositories we would like to download charts. Because we will be downloading only one — &lt;em&gt;stable/nginx-ingress&lt;/em&gt; — chart from a single repo, we put one entry:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes-charts.storage.googleapis.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then we can move on to next section — defining the &lt;em&gt;Helm&lt;/em&gt; releases. It will contain a list of all applications that &lt;em&gt;helmfile&lt;/em&gt; will need to deploy. Starting from the first one — &lt;code&gt;postgres&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/postgres&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/kanban-postgres.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;There is not that much here, but for clarity:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;name&lt;/code&gt; — is a name of a release, we can name is whatever we like,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;chart&lt;/code&gt; — tells &lt;em&gt;helmfile&lt;/em&gt; where a base &lt;em&gt;Helm&lt;/em&gt; chart is located,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;values&lt;/code&gt; — because we’re using a &lt;em&gt;Helm&lt;/em&gt; chart template, we would like to inject some values into it, so here is a list of &lt;em&gt;YAML&lt;/em&gt; files that holds them.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, let’s define releases that are based on app chart:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/app&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/adminer.yaml&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-app&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/app&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/kanban-app.yaml&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/app&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/kanban-ui.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And next, we define a &lt;em&gt;Helm&lt;/em&gt; release for an &lt;em&gt;Ingress&lt;/em&gt; backend, which will be downloaded from a public repository:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-backend&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable/nginx-ingress&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.36.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;In a &lt;code&gt;chart&lt;/code&gt; section we provide information of which chart we would like to download and install. The &lt;code&gt;stable/&lt;/code&gt; prefix is a name of a repository that was defined in a &lt;code&gt;repositories&lt;/code&gt; few moments ago.&lt;/p&gt;

&lt;p&gt;In version we specify which version of a chart to use.&lt;/p&gt;

&lt;p&gt;And finally a definition for &lt;em&gt;Ingress Controller Helm&lt;/em&gt; release:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-controller&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/ingress&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/ingress.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And if we merge all of them, &lt;em&gt;helmfile.yaml&lt;/em&gt; will look as follows:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes-charts.storage.googleapis.com&lt;/span&gt;

&lt;span class="na"&gt;releases&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/postgres&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/kanban-postgres.yaml&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/app&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/adminer.yaml&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-app&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/app&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/kanban-app.yaml&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/app&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/kanban-ui.yaml&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-backend&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stable/nginx-ingress&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.36.0&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-controller&lt;/span&gt;
    &lt;span class="na"&gt;chart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./charts/ingress&lt;/span&gt;
    &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./values/ingress.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And now it is a fun part, with just two commands we can deploy all applications.&lt;/p&gt;

&lt;p&gt;First, we need to add the repository to our instance of Helm (you’ll need to do that only once):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helmfile repos
Adding repo stable &lt;span class="o"&gt;[&lt;/span&gt;https://kubernetes-charts.storage.googleapis.com]&lt;span class="o"&gt;(&lt;/span&gt;https://kubernetes-charts.storage.googleapis.com&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="s2"&gt;"stable"&lt;/span&gt; has been added to your repositories

Updating repo
Hang tight &lt;span class="k"&gt;while &lt;/span&gt;we grab the latest from your chart repositories...
...Successfully got an update from the &lt;span class="s2"&gt;"stable"&lt;/span&gt; chart repository
Update Complete. ⎈ Happy Helming!⎈
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then install all charts on a cluster:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helmfile &lt;span class="nb"&gt;sync
&lt;/span&gt;Adding repo stable &lt;span class="o"&gt;[&lt;/span&gt;https://kubernetes-charts.storage.googleapis.com]&lt;span class="o"&gt;(&lt;/span&gt;https://kubernetes-charts.storage.googleapis.com&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="s2"&gt;"stable"&lt;/span&gt; has been added to your repositories

Updating repo
Hang tight &lt;span class="k"&gt;while &lt;/span&gt;we grab the latest from your chart repositories...
...Successfully got an update from the &lt;span class="s2"&gt;"stable"&lt;/span&gt; chart repository
Update Complete. ⎈ Happy Helming!⎈

Building dependency &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/postgres
Building dependency &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;adminer, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/app
Building dependency &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kanban-app, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/app
Building dependency &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kanban-ui, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/app
Building dependency &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ingress-controller, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/ingress
Affected releases are:
  adminer &lt;span class="o"&gt;(&lt;/span&gt;./charts/app&lt;span class="o"&gt;)&lt;/span&gt; UPDATED
  ingress-backend &lt;span class="o"&gt;(&lt;/span&gt;stable/nginx-ingress&lt;span class="o"&gt;)&lt;/span&gt; UPDATED
  ingress-controller &lt;span class="o"&gt;(&lt;/span&gt;./charts/ingress&lt;span class="o"&gt;)&lt;/span&gt; UPDATED
  kanban-app &lt;span class="o"&gt;(&lt;/span&gt;./charts/app&lt;span class="o"&gt;)&lt;/span&gt; UPDATED
  kanban-ui &lt;span class="o"&gt;(&lt;/span&gt;./charts/app&lt;span class="o"&gt;)&lt;/span&gt; UPDATED
  postgres &lt;span class="o"&gt;(&lt;/span&gt;./charts/postgres&lt;span class="o"&gt;)&lt;/span&gt; UPDATED

Upgrading &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgres, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/postgres
Upgrading &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kanban-app, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/app
Upgrading &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;adminer, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/app
Upgrading &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kanban-ui, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/app
Upgrading &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ingress-controller, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;charts/ingress
Upgrading &lt;span class="nv"&gt;release&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ingress-backend, &lt;span class="nv"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;stable/nginx-ingress
Release &lt;span class="s2"&gt;"adminer"&lt;/span&gt; does not exist. Installing it now.
NAME: adminer
LAST DEPLOYED: Wed Apr 15 16:16:31 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Listing releases matching ^adminer&lt;span class="err"&gt;$&lt;/span&gt;
Release &lt;span class="s2"&gt;"kanban-ui"&lt;/span&gt; does not exist. Installing it now.
NAME: kanban-ui
LAST DEPLOYED: Wed Apr 15 16:16:31 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Listing releases matching ^kanban-ui&lt;span class="err"&gt;$&lt;/span&gt;
adminer default   1        2020-04-15 16:16:31.990139954 +0200 CEST deployed app-0.1.0 1.16.0

Release &lt;span class="s2"&gt;"kanban-app"&lt;/span&gt; does not exist. Installing it now.
NAME: kanban-app
LAST DEPLOYED: Wed Apr 15 16:16:31 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Listing releases matching ^kanban-app&lt;span class="err"&gt;$&lt;/span&gt;
kanban-ui default   1        2020-04-15 16:16:31.968291217 +0200 CEST deployed app-0.1.0 1.16.0

Release &lt;span class="s2"&gt;"postgres"&lt;/span&gt; does not exist. Installing it now.
NAME: postgres
LAST DEPLOYED: Wed Apr 15 16:16:31 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Listing releases matching ^postgres&lt;span class="err"&gt;$&lt;/span&gt;
kanban-app default   1        2020-04-15 16:16:31.958860307 +0200 CEST deployed app-0.1.0 1.16.0

postgres default   1        2020-04-15 16:16:31.958951797 +0200 CEST deployed postgres-0.1.0 1.16.0

Release &lt;span class="s2"&gt;"ingress-controller"&lt;/span&gt; does not exist. Installing it now.
NAME: ingress-controller
LAST DEPLOYED: Wed Apr 15 16:16:31 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

Listing releases matching ^ingress-controller&lt;span class="err"&gt;$&lt;/span&gt;
ingress-controller default   1        2020-04-15 16:16:31.958844983 +0200 CEST deployed ingress-0.1.0 1.16.0

Release &lt;span class="s2"&gt;"ingress-backend"&lt;/span&gt; does not exist. Installing it now.
NAME: ingress-backend
LAST DEPLOYED: Wed Apr 15 16:16:35 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
The nginx-ingress controller has been installed.
It may take a few minutes &lt;span class="k"&gt;for &lt;/span&gt;the LoadBalancer IP to be available.
You can watch the status by running &lt;span class="s1"&gt;'kubectl --namespace default get services -o wide -w ingress-backend-nginx-ingress-controller'&lt;/span&gt;

An example Ingress that makes use of the controller:

apiVersion: extensions/v1beta1
  kind: Ingress
  metadata:
    annotations:
      kubernetes.io/ingress.class: nginx
    name: example
    namespace: foo
  spec:
    rules:
      - host: &lt;span class="o"&gt;[&lt;/span&gt;www.example.com]&lt;span class="o"&gt;(&lt;/span&gt;http://www.example.com&lt;span class="o"&gt;)&lt;/span&gt;
        http:
          paths:
            - backend:
                serviceName: exampleService
                servicePort: 80
                path: /
        &lt;span class="c"&gt;# This section is only required if TLS is to be enabled for the Ingress&lt;/span&gt;
    tls:
        - hosts:
            - &lt;span class="o"&gt;[&lt;/span&gt;www.example.com]&lt;span class="o"&gt;(&lt;/span&gt;http://www.example.com&lt;span class="o"&gt;)&lt;/span&gt;
          secretName: example-tls

If TLS is enabled &lt;span class="k"&gt;for &lt;/span&gt;the Ingress, a Secret containing the certificate and key must also be provided:

apiVersion: v1
  kind: Secret
  metadata:
    name: example-tls
    namespace: foo
  data:
    tls.crt: &amp;lt;&lt;span class="nb"&gt;base64 &lt;/span&gt;encoded cert&amp;gt;
    tls.key: &amp;lt;&lt;span class="nb"&gt;base64 &lt;/span&gt;encoded key&amp;gt;
  &lt;span class="nb"&gt;type&lt;/span&gt;: kubernetes.io/tls

Listing releases matching ^ingress-backend&lt;span class="err"&gt;$&lt;/span&gt;
ingress-backend default   1        2020-04-15 16:16:35.070513181 +0200 CEST deployed nginx-ingress-1.36.0 0.30.0

UPDATED RELEASES:
NAME                 CHART                  VERSION
adminer              ./charts/app             0.1.0
kanban-ui            ./charts/app             0.1.0
kanban-app           ./charts/app             0.1.0
postgres             ./charts/postgres        0.1.0
ingress-controller   ./charts/ingress         0.1.0
ingress-backend      stable/nginx-ingress    1.36.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After couple minutes you will be able to enter &lt;a href="http://kanban.k8s.com" rel="noopener noreferrer"&gt;http://kanban.k8s.com&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And that’s it! Very simple and everything — external repository set up and a list of each &lt;em&gt;Helm&lt;/em&gt; releases is in one single file, which can be stored under version control system ( &lt;em&gt;git&lt;/em&gt; for example).&lt;/p&gt;

&lt;p&gt;And what is more to mention theses are not the only features of &lt;em&gt;helmfile&lt;/em&gt;. More of them you can find in &lt;a href="https://github.com/roboll/helmfile" rel="noopener noreferrer"&gt;the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To sum up the entire series, I hope you’ve learn something and you find out what are the difference for deployments in native &lt;em&gt;Kubernetes&lt;/em&gt;, &lt;em&gt;Helm&lt;/em&gt; and &lt;em&gt;helmfile&lt;/em&gt;. I hope it’s more clear for you how each one of them look like and what are they pros and cons. You may comment it here or in other blog post what do you like and what you think is worth to correct from my side. I would be grateful.&lt;/p&gt;

&lt;p&gt;As usual, here is a link to a source code — to this project:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec" rel="noopener noreferrer"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/k8s-helm-helmfile" rel="noopener noreferrer"&gt;
        k8s-helm-helmfile
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Project which compares 3 approaches to deploy apps on Kubernetes cluster (using kubectl, helm &amp;amp; helmfile)
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;And to kanban-board project:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/wkrzywiec" rel="noopener noreferrer"&gt;
        wkrzywiec
      &lt;/a&gt; / &lt;a href="https://github.com/wkrzywiec/kanban-board" rel="noopener noreferrer"&gt;
        kanban-board
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Single-click full-stack application (Postgres, Spring Boot &amp;amp; Angular) using Docker Compose
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/roboll/helmfile" rel="noopener noreferrer"&gt;&lt;strong&gt;roboll/helmfile&lt;/strong&gt; on github.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/cloudposse/helmfiles" rel="noopener noreferrer"&gt;&lt;strong&gt;cloudposse/helmfiles&lt;/strong&gt; on github.com&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.reddit.com/r/kubernetes/comments/79wlj1/helmfile_a_declarative_spec_for_deploying_helm/?utm_content=title&amp;amp;utm_medium=post_embed&amp;amp;utm_name=b111903d60dd41d69e11f3ad1063be54&amp;amp;utm_source=embedly&amp;amp;utm_term=79wlj1" rel="noopener noreferrer"&gt;&lt;strong&gt;helmfile - a declarative spec for deploying helm charts&lt;/strong&gt; on reddit.com&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>helm</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to deploy application on Kubernetes with Helm</title>
      <dc:creator>Wojtek Krzywiec</dc:creator>
      <pubDate>Thu, 14 Jan 2021 10:34:46 +0000</pubDate>
      <link>https://dev.to/wkrzywiec/how-to-deploy-application-on-kubernetes-with-helm-8p</link>
      <guid>https://dev.to/wkrzywiec/how-to-deploy-application-on-kubernetes-with-helm-8p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Cover image by &lt;a href="https://unsplash.com/@andrewtneel" rel="noopener noreferrer"&gt;Andrew Neel&lt;/a&gt; on &lt;a href="https://unsplash.com" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;In this blog post I present step-by-step how to deploy multiple applications on Kubernetes cluster using Helm.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a second part of my series on Kubernetes. It compares three approaches of deploying applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;with &lt;strong&gt;kubectl&lt;/strong&gt; — &lt;a href="https://dev.to/wkrzywiec/deployment-of-multiple-apps-on-kubernetes-cluster-walkthrough-32km"&gt;Deployment of multiple apps on Kubernetes cluster — Walkthrough&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;with &lt;strong&gt;Helm&lt;/strong&gt; — this one,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;with &lt;strong&gt;helmfile&lt;/strong&gt; — &lt;a href="https://dev.to/wkrzywiec/how-to-declaratively-run-helm-charts-using-helmfile-4ld8"&gt;How to declaratively run Helm charts using helmfile&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven’t read it first one, I would advise to do that and then go back to this post. But if you prefer to jump right away to this post, don’t worry, I’ll briefly introduce you to the project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fckgv04imrimtukgqn4er.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fckgv04imrimtukgqn4er.png" alt="cluster"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above picture presents the target solution. Inside the cluster there will be deployed frontend (&lt;em&gt;kanban-ui&lt;/em&gt;) and backend (&lt;em&gt;kanban-app&lt;/em&gt;) services together with postgres database. A source code for both microservices can be found in my GitHub repository — &lt;a href="https://github.com/wkrzywiec/kanban-board" rel="noopener noreferrer"&gt;kanban-board&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Additionally I’ve added an &lt;em&gt;adminer&lt;/em&gt;, which is a GUI client for getting inside the database.&lt;/p&gt;

&lt;p&gt;If we focus on one of services, e.g. on &lt;em&gt;kanban-ui&lt;/em&gt; we can see that it needs to create two Kubernetes objects — &lt;em&gt;ClusterIP&lt;/em&gt; &amp;amp; &lt;em&gt;Deployment&lt;/em&gt;. With plain approach, using kubectl we would needed to create two files for each Kubernetes object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
        &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wkrzywiec/kanban-ui:k8s&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The same story is for any other application — they need at least two definition files. Some of them requires even more, like postgres, which needs to have &lt;em&gt;PersistentVolumeClaims&lt;/em&gt; defined in addition. As a result for even small project we can end up with lots of files which are very similar to each other:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi33b21k4m9gxzzjpk3l3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fi33b21k4m9gxzzjpk3l3.png" alt="definitions"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How to achieve the DRY in this case? Is it possible?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To reduce the number of YAML files we could merge them into one, for example, for &lt;em&gt;kanban-ui&lt;/em&gt; it will look like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
      &lt;span class="na"&gt;targetPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
        &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
          &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wkrzywiec/kanban-ui:k8s&lt;/span&gt;
          &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;But it still doesn’t fix the major problem — how to avoid copy-pasting entire file just to replace couple of values? It would be great if there is a way to define a blueprint for both objects and then inject values into specific fields.&lt;/p&gt;

&lt;p&gt;Luckily there is a solution! &lt;a href="https://helm.sh" rel="noopener noreferrer"&gt;&lt;strong&gt;Helm&lt;/strong&gt;&lt;/a&gt; to the rescue!&lt;/p&gt;

&lt;p&gt;Accordingly to the official website — &lt;em&gt;Helm&lt;/em&gt; is a package manager for &lt;em&gt;Kubernetes&lt;/em&gt;. It helps deploy complex application by bundling necessary resources into &lt;strong&gt;Charts&lt;/strong&gt;, which contains all information to run application on a cluster.&lt;/p&gt;

&lt;p&gt;There are couple approaches how to work with &lt;em&gt;Helm&lt;/em&gt;. One of them is to download publicly available charts from the &lt;a href="https://hub.helm.sh" rel="noopener noreferrer"&gt;Helm Hub&lt;/a&gt;. They are prepared by community and are free to use.&lt;/p&gt;

&lt;p&gt;For instance, if we would like to run &lt;a href="https://prometheus.io" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt; on a cluster it would just easy as it’s described on this page — &lt;a href="https://hub.helm.sh/charts/stable/prometheus" rel="noopener noreferrer"&gt;https://hub.helm.sh/charts/stable/prometheus&lt;/a&gt; — with the single command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm &lt;span class="nb"&gt;install &lt;/span&gt;stable/prometheus
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It contains some default configuration, but can be easily overridden with YAML file and passed during installation. The detailed example I’ll show in a minute.&lt;/p&gt;

&lt;p&gt;But &lt;em&gt;Helm&lt;/em&gt; is not only providing some predefined blueprints, you can create your own charts!&lt;/p&gt;

&lt;p&gt;It’s very easy and can be done by a single command &lt;code&gt;helm create &amp;lt;chart-name&amp;gt;&lt;/code&gt; , which creates a folder with a basic structure:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm create example
Creating example
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe4c9iyaelmygx3i7z8cp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fe4c9iyaelmygx3i7z8cp.png" alt="helm chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the templates/ folder there are &lt;strong&gt;Helm templates&lt;/strong&gt; that with combination of values.yaml will result in set of Kubernetes objects.&lt;/p&gt;

&lt;p&gt;Let’s create a first chart — &lt;strong&gt;postgres&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But just before that, boring installations and configurations (but promise, it’ll go quick). In my demo I’m using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://docs.docker.com/install/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;locally installed Kubernetes cluster — &lt;a href="https://kubernetes.io/docs/tasks/tools/install-minikube/" rel="noopener noreferrer"&gt;minikube&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Kubernetes command line tool —&lt;a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" rel="noopener noreferrer"&gt; kubectl&lt;/a&gt;,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://helm.sh/docs/intro/install/" rel="noopener noreferrer"&gt;Helm (v3)&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When everything is installed you can start up the minikube cluster and enable ingress addon:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; minikube start

😄  minikube v1.8.1 on Ubuntu 18.04
✨  Automatically selected the docker driver
🔥  Creating Kubernetes &lt;span class="k"&gt;in &lt;/span&gt;docker container with &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;CPUs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;8 available&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="nv"&gt;Memory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2200MB &lt;span class="o"&gt;(&lt;/span&gt;7826MB available&lt;span class="o"&gt;)&lt;/span&gt; ...
🐳  Preparing Kubernetes v1.17.3 on Docker 19.03.2 ...
▪ kubeadm.pod-network-cidr&lt;span class="o"&gt;=&lt;/span&gt;10.244.0.0/16
❌  Unable to load cached images: loading cached images: &lt;span class="nb"&gt;stat&lt;/span&gt; /home/wojtek/.minikube/cache/images/k8s.gcr.io/kube-proxy_v1.17.3: no such file or directory
🚀  Launching Kubernetes ...
🌟  Enabling addons: default-storageclass, storage-provisioner
⌛  Waiting &lt;span class="k"&gt;for &lt;/span&gt;cluster to come online ...
🏄  Done! kubectl is now configured to use &lt;span class="s2"&gt;"minikube"&lt;/span&gt;

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; minikube addons &lt;span class="nb"&gt;enable &lt;/span&gt;ingress
🌟  The &lt;span class="s1"&gt;'ingress'&lt;/span&gt; addon is enabled
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then you’ll need to edit you &lt;strong&gt;hosts&lt;/strong&gt; file. Its location varies on OS:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="http://manpages.ubuntu.com/manpages/trusty/man5/hosts.5.html" rel="noopener noreferrer"&gt;Ubuntu&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.groovypost.com/howto/edit-hosts-file-windows-10/" rel="noopener noreferrer"&gt;Windows&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.imore.com/how-edit-your-macs-hosts-file-and-why-you-would-want#page1" rel="noopener noreferrer"&gt;MacOS&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you find it add following lines:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;172.17.0.2  adminer.k8s.com
172.17.0.2  kanban.k8s.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To make sure that this config is correct you’ll need to check if an IP address of &lt;em&gt;minikube&lt;/em&gt; cluster on your machine is the same as it’s above. In order to do that run the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; minikube ip
172.17.0.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we can create first Helm chart:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm create postgres
Creating postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We will not need generated files inside &lt;code&gt;./templates&lt;/code&gt; folder, so remove them and also clear all the content inside &lt;strong&gt;values.yaml&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now we can roll up our sleeves and define all necessary files to create chart for postgres database. For a reminder, in order to deploy it with &lt;em&gt;kubectl&lt;/em&gt; we had to have following files:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo4jdmxf8a3dvq7x0f3go.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fo4jdmxf8a3dvq7x0f3go.png" alt="postgres files"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They contain definitions of &lt;em&gt;ConfigMap, Deployment, PersistentVolumeClaim&lt;/em&gt; and &lt;em&gt;ClusterIP&lt;/em&gt;. Their full definitions could be found &lt;a href="https://github.com/wkrzywiec/k8s-helm-helmfile/tree/master/k8s" rel="noopener noreferrer"&gt;in a repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;First, let’s create a template for postgres &lt;em&gt;Deployment&lt;/em&gt; object, therefore inside the &lt;code&gt;./templates&lt;/code&gt; create a &lt;strong&gt;deployment.yaml&lt;/strong&gt; file:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;At first glance you might see these strange parts between two pairs of curly brackets, like &lt;code&gt;{{ .Values.postgres.name }}&lt;/code&gt; . They are written in &lt;a href="https://golang.org/pkg/text/template/" rel="noopener noreferrer"&gt;Go template language&lt;/a&gt; and are referring to a value located in a &lt;strong&gt;values.yaml&lt;/strong&gt; which is located inside the root folder of a chart. For mentioned example Helm will try to match it with a value from &lt;em&gt;values.yaml&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Another example. A value for a base Docker image, defined in image: &lt;code&gt;{{ .Values.postgres.container.image }}&lt;/code&gt; will be taken from:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And so on. We can define the structure inside this file whatever we like.&lt;/p&gt;

&lt;p&gt;This database Deployment requires a &lt;em&gt;PersistentVolumeClaim&lt;/em&gt; to be created to reserve some storage on a disk, therefore we need to create a Helm template — &lt;strong&gt;pvc.yaml&lt;/strong&gt; inside the &lt;code&gt;./templates&lt;/code&gt; folder:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;This one is shorter, and there is nothing new here.&lt;/p&gt;

&lt;p&gt;Next template that we need to create is for &lt;em&gt;ClusterIP&lt;/em&gt;, which will allow other &lt;em&gt;Pods&lt;/em&gt; inside the cluster to enter the* Pod with postgres — &lt;strong&gt;service.yaml&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And finally &lt;em&gt;ConfigMap&lt;/em&gt; template needs to be created — &lt;strong&gt;config.yaml&lt;/strong&gt;. It’ll hold information about created database, user and its password (yes, not very secure, but for simple example it’s enough).&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here you might see a strange &lt;code&gt;{{ -range ... }}&lt;/code&gt; clause, which can be translated as a for each loop known in any programming language. In above example, Helm template will try to inject values from an array defined in &lt;em&gt;values.yaml&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;key&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside a template, there are only dummy values. If you want to have different ones you would need to override them during installation of this set of objects on a Kubernetes cluster.&lt;/p&gt;

&lt;p&gt;The entire &lt;em&gt;values.yaml&lt;/em&gt; presents as follow:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6-alpine&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5432&lt;/span&gt;
    &lt;span class="na"&gt;volume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-storage&lt;/span&gt;
        &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PersistentVolumeClaim&lt;/span&gt;
        &lt;span class="na"&gt;mountPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/lib/postgresql/data&lt;/span&gt;
        &lt;span class="na"&gt;pvc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-persistent-volume-claim&lt;/span&gt;
          &lt;span class="na"&gt;accessMode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ReadWriteOnce&lt;/span&gt;
          &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;4Gi&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-config&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;key&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;To finalize creating first chart we need to modify some metadata inside &lt;strong&gt;Chart.yaml&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v2&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Helm chart for PostgreSQL database&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.1.0&lt;/span&gt;
&lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.16.0&lt;/span&gt;
&lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;database&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/wkrzywiec/k8s-helm-helmfile/tree/master/helm&lt;/span&gt;
&lt;span class="na"&gt;maintainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wojtek Krzywiec&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/wkrzywiec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then create a new &lt;strong&gt;kanban-postgres.yaml&lt;/strong&gt; file which will hold some specific values. Put it outside the &lt;code&gt;postgres&lt;/code&gt; chart folder so the file structure can be similar to that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw56u3502tqrr04fvhpon.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw56u3502tqrr04fvhpon.png" alt="postgres chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the file put only values that will be specific for this deployment, i.e. postgres database credentials (all other values we can keep as default):&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Everything is set up, we can now deploy postgres into a cluster. In a Helm world this step is called creating a new &lt;strong&gt;release&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; kanban-postgres.yaml postgres ./postgres
NAME: postgres
LAST DEPLOYED: Mon Apr 13 16:13:16 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm list
NAME     NAMESPACE REVISION  STATUS    CHART          APP VERSION
postgres default   1         deployed  postgres-0.1.0 1.16.0

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; kubectl get deployments
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
postgres   1/1     1            1           2m14s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Cool, the postgres is up and running! Before moving forward let’s have a closer look on helm install command, what each of an argument means:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F11c3sxgkvr540ypg0xhy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F11c3sxgkvr540ypg0xhy.png" alt="helm install"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you plan to use a chart only to create one Helm release you might wonder what is a value behind using Helm at all.&lt;/p&gt;

&lt;p&gt;Let me show you that on another example. Let’s create a new chart called &lt;strong&gt;app&lt;/strong&gt; — it will be a template chart for three different releases — &lt;em&gt;adminer&lt;/em&gt;, &lt;em&gt;kanban-app&lt;/em&gt; &amp;amp; &lt;em&gt;kanban-ui&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm create app
Creating app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;After removing unnecessary file from &lt;code&gt;./templates&lt;/code&gt; folder and cleaning the &lt;strong&gt;values.yaml&lt;/strong&gt; create a &lt;strong&gt;deployment.yaml&lt;/strong&gt; file:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Nothing new in particular, only some placeholders for values &amp;amp; two range loops to inject values from *ConfigMap*s or simple values to application container.&lt;/p&gt;

&lt;p&gt;All 3 apps require to have also the &lt;em&gt;ClusterIP&lt;/em&gt; objects to be deployed, therefore here is its template — &lt;strong&gt;service.yaml&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;All default values in both templates are injected from &lt;strong&gt;values.yaml&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;add-image-here&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;key&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;value&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And to close a task of creating app chart — we need to define some metadata in the &lt;strong&gt;Chart.yaml&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v2&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Helm chart for any application&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.1.0&lt;/span&gt;
&lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.16.0&lt;/span&gt;
&lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;java&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;javascript&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;angular&lt;/span&gt;
&lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/wkrzywiec/k8s-helm-helmfile/tree/master/helm&lt;/span&gt;
&lt;span class="na"&gt;maintainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wojtek Krzywiec&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/wkrzywiec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then, similarly to previous example, outside the app folder create YAML files with values which will override ones from &lt;em&gt;values.yaml&lt;/em&gt; inside app chart.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;adminer.yaml&lt;/strong&gt; file looks as follow:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer:4.7.6-standalone&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ADMINER_DESIGN&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pepa-linha&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ADMINER_DEFAULT_SERVER&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A &lt;strong&gt;kanban-app.yaml&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-app&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;backend&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wkrzywiec/kanban-app:k8s&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres-config&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_SERVER&lt;/span&gt;
        &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A &lt;strong&gt;kanban-ui.yaml&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
  &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;frontend&lt;/span&gt;
  &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wkrzywiec/kanban-ui:k8s&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A resulting file structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm2r3s6tzhz5mfffouajs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm2r3s6tzhz5mfffouajs.png" alt="file structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, in order to create three releases for each application use commands:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; adminer.yaml adminer ./app
NAME: adminer
LAST DEPLOYED: Mon Apr 13 16:57:17 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; kanban-app.yaml kanban-app ./app
NAME: kanban-app
LAST DEPLOYED: Mon Apr 13 16:57:36 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; kanban-ui.yaml kanban-ui ./app
NAME: kanban-ui
LAST DEPLOYED: Mon Apr 13 16:57:54 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm list
NAME       NAMESPACE REVISION  STATUS    CHART           APP VERSION
adminer    default   1         deployed  app-0.1.0       1.16.0     
kanban-app default   1         deployed  app-0.1.0       1.16.0     
kanban-ui  default   1         deployed  app-0.1.0       1.16.0     
postgres   default   1         deployed  postgres-0.1.0  1.16.0

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; kubectl get deployments
NAME         READY   UP-TO-DATE   AVAILABLE   AGE
adminer      1/1     1            1           112s
kanban-app   1/1     1            1           93s
kanban-ui    1/1     1            1           75s
postgres     1/1     1            1           45m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Perfect! There is one more thing to do — create a chart with &lt;em&gt;Ingress Controller&lt;/em&gt; — a gateway to a cluster.&lt;/p&gt;

&lt;p&gt;Like before, create a new chart:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm create ingress
Creating ingress
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Remove all files from templates folder and clear content of &lt;em&gt;values.yaml&lt;/em&gt;. This time, before moving straight away to defining a templates, let’s focus on to &lt;strong&gt;Chart.yaml&lt;/strong&gt; first.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v2&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;A Helm chart for Ingress Controller&lt;/span&gt;
&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0.1.0&lt;/span&gt;
&lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.16.0&lt;/span&gt;
&lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ingress&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;api-gateway&lt;/span&gt;
&lt;span class="na"&gt;home&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/wkrzywiec/k8s-helm-helmfile/tree/master/helm&lt;/span&gt;
&lt;span class="na"&gt;maintainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Wojtek Krzywiec&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/wkrzywiec&lt;/span&gt;
&lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx-ingress&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.36.0&lt;/span&gt;
    &lt;span class="na"&gt;repository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://kubernetes-charts.storage.googleapis.com/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here there is a new section — &lt;code&gt;dependencies&lt;/code&gt;— which has been added. It creates default backend services which enables the &lt;em&gt;Ingress Controller&lt;/em&gt; features.&lt;/p&gt;

&lt;p&gt;But it’s not the only thing we need to do here. This section only defines on what this chart depends on, it won’t download it automatically during the installation. We need to take care of it ourselves. To do that run the command:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm dependency update ./ingress/
Hang tight &lt;span class="k"&gt;while &lt;/span&gt;we grab the latest from your chart repositories...
...Successfully got an update from the &lt;span class="s2"&gt;"stable"&lt;/span&gt; chart repository
Update Complete. ⎈Happy Helming!⎈
Saving 1 charts
Downloading nginx-ingress from repo &lt;span class="o"&gt;[&lt;/span&gt;https://kubernetes-charts.storage.googleapis.com/]&lt;span class="o"&gt;(&lt;/span&gt;https://kubernetes-charts.storage.googleapis.com/&lt;span class="o"&gt;)&lt;/span&gt;
Deleting outdated charts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Inside the &lt;code&gt;ingress/charts&lt;/code&gt; folder a new file will appear — &lt;code&gt;nginx-ingress-1.36.0.tgz&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;Now we can define a template for Ingress — it will be located inside &lt;code&gt;./templates&lt;/code&gt; folder and will be called &lt;strong&gt;ingress.yaml&lt;/strong&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;The most interesting part is inside specification section. There are two nested loops there which allow to define multiple hosts and multiple paths for each host.&lt;/p&gt;

&lt;p&gt;And also, here is a default &lt;strong&gt;values.yaml&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingress-service&lt;/span&gt;
  &lt;span class="na"&gt;replicaCount&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
    &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chart-example.local&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;serviceName&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting folder structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsqiwsyhypd72rw0lh082.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsqiwsyhypd72rw0lh082.png" alt="ingress chart"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Outside the &lt;code&gt;ingress&lt;/code&gt; chart we can now create an &lt;em&gt;ingress.yaml&lt;/em&gt; file which will hold all routing rules for our cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;ingress&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer.k8s.com&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;adminer&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban.k8s.com&lt;/span&gt;
      &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/api/&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-app&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
          &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;serviceName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kanban-ui&lt;/span&gt;
            &lt;span class="na"&gt;servicePort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we’re able to create a Helm release:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ingress.yaml ingress ./ingress
NAME: ingress
LAST DEPLOYED: Tue Apr 14 07:22:44 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; helm list
NAME       NAMESPACE  REVISION  STATUS    CHART          APP VERSION
adminer    default    1         deployed  app-0.1.0      1.16.0     
ingress    default    1         deployed  ingress-0.1.0  1.16.0     
kanban-app default    1         deployed  app-0.1.0      1.16.0     
kanban-ui  default    1         deployed  app-0.1.0      1.16.0     
postgres   default    1         deployed  postgres-0.1.0 1.16.0

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; kubectl get deployments
NAME                                    READY  UP-TO-DATE  AVAILABLE
adminer                                 1/1    1           1           
ingress-nginx-ingress-controller        1/1    1           1           
ingress-nginx-ingress-default-backend   1/1    1           1           
kanban-app                              1/1    1           1           
kanban-ui                               1/1    1           1           
postgres                                1/1    1           1           
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything is set up, you can start testing it. Go to the &lt;a href="http://kanban.k8s.com" rel="noopener noreferrer"&gt;http://kanban.k8s.com&lt;/a&gt; and you should get this page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ficmrf5t4lmmq0i0abnh7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ficmrf5t4lmmq0i0abnh7.png" alt="kanban-ui"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Congrats! 🍾🤘&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;In this short entry I’ve presented how, with Helm, you can reduce of copy-pasting tasks and can bring a one single template for deploying multiple applications on a Kubernetes cluster, which might be very handy in a microservice world. I hope you’ve enjoyed it.&lt;/p&gt;

&lt;p&gt;But there is still one thing left, which holds us from establishing a fully declarative approach for an infrastructure. In order to deploy each application we still need to run imperative commands, like helm install. But luckily there is another tool — helmfile! But this one I’ll describe in my next story.&lt;/p&gt;

&lt;p&gt;For now, here is my GitHub repository with a source code for this project:&lt;br&gt;
&lt;a href="https://github.com/wkrzywiec/k8s-helm-helmfile" rel="noopener noreferrer"&gt;&lt;strong&gt;wkrzywiec/k8s-helm-helmfile&lt;/strong&gt; on github.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here is a source code of Kanban project:&lt;br&gt;
&lt;a href="https://github.com/wkrzywiec/kanban-board" rel="noopener noreferrer"&gt;&lt;strong&gt;wkrzywiec/kanban-board&lt;/strong&gt; on github.com&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://helm.sh/docs/" rel="noopener noreferrer"&gt;&lt;strong&gt;Docs Home&lt;/strong&gt; on helm.sh&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codefresh.io/docs/docs/new-helm/helm-best-practices/" rel="noopener noreferrer"&gt;&lt;strong&gt;HELM Best practices&lt;/strong&gt; on codefresh.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://akomljen.com/package-kubernetes-applications-with-helm/" rel="noopener noreferrer"&gt;&lt;strong&gt;Package Kubernetes Applications with Helm&lt;/strong&gt; on akomljen.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://itnext.io/drastically-improve-your-kubernetes-deployments-with-helm-5323e7f11ef8" rel="noopener noreferrer"&gt;&lt;strong&gt;Drastically Improve your Kubernetes Deployments with Helm&lt;/strong&gt; on itnext.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/alexellis/helm3-expressjs-tutorial" rel="noopener noreferrer"&gt;&lt;strong&gt;alexellis/helm3-expressjs-tutorial&lt;/strong&gt; on github.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>kubernetes</category>
      <category>helm</category>
      <category>beginners</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
