<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://nurgasemetey.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://nurgasemetey.com/" rel="alternate" type="text/html" hreflang="en-US" /><updated>2025-11-29T18:35:19+03:00</updated><id>https://nurgasemetey.com/feed.xml</id><title type="html">Nurgazy Nazhimidinov’s Personal Website</title><subtitle>Trying</subtitle><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><entry><title type="html">How my lack of knowledge of mocking in Scala Spark applications was affecting code structure</title><link href="https://nurgasemetey.com/how-my-lack-of-knowledge-of-mocking-in-scala-spark-applications-was-affecting-code-structure/" rel="alternate" type="text/html" title="How my lack of knowledge of mocking in Scala Spark applications was affecting code structure" /><published>2024-09-25T03:00:00+03:00</published><updated>2024-09-25T03:00:00+03:00</updated><id>https://nurgasemetey.com/how-my-lack-of-knowledge-of-mocking-in-scala-spark-applications-was-affecting-code-structure</id><content type="html" xml:base="https://nurgasemetey.com/how-my-lack-of-knowledge-of-mocking-in-scala-spark-applications-was-affecting-code-structure/"><![CDATA[<p>I was working with Scala Spark applications recently. Because I didn’t know much about mocking table reads in Spark and had to write a unit test for a function, I removed all the <code class="language-plaintext highlighter-rouge">spark.read</code> calls from the function. When I looked at the function to be tested, I noticed that it had become less cohesive.</p>

<p>Here is the version of the function without <code class="language-plaintext highlighter-rouge">spark.read</code> calls:</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">processThirdPartyEvents</span><span class="o">(</span><span class="n">entities</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">,</span> <span class="n">subEntities</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">,</span> <span class="n">entityEvents</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">,</span> <span class="n">thirdPartyEvents</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">)</span>
</code></pre></div></div>

<p>And how it is used</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">val</span> <span class="nv">entityEventTable</span> <span class="k">=</span> <span class="nv">applicationConfig</span><span class="o">.</span><span class="py">getString</span><span class="o">(</span><span class="s">"entity_event_table"</span><span class="o">)</span>
<span class="k">val</span> <span class="nv">entityEvents</span> <span class="k">=</span> <span class="nf">getEntityEvents</span><span class="o">(</span><span class="n">entityEventTable</span><span class="o">,</span> <span class="nv">jobConfig</span><span class="o">.</span><span class="py">year</span><span class="o">,</span> <span class="nv">jobConfig</span><span class="o">.</span><span class="py">month</span><span class="o">,</span> <span class="nv">jobConfig</span><span class="o">.</span><span class="py">day</span><span class="o">).</span><span class="py">cache</span><span class="o">()</span>

<span class="k">val</span> <span class="nv">thirdPartyEventTable</span> <span class="k">=</span> <span class="nv">applicationConfig</span><span class="o">.</span><span class="py">getString</span><span class="o">(</span><span class="s">"third_party_event_table"</span><span class="o">)</span>
<span class="k">val</span> <span class="nv">startDate</span> <span class="k">=</span> <span class="nv">LocalDate</span><span class="o">.</span><span class="py">of</span><span class="o">(</span><span class="nv">jobConfig</span><span class="o">.</span><span class="py">year</span><span class="o">.</span><span class="py">toInt</span><span class="o">,</span> <span class="nv">jobConfig</span><span class="o">.</span><span class="py">month</span><span class="o">.</span><span class="py">toInt</span><span class="o">,</span> <span class="nv">jobConfig</span><span class="o">.</span><span class="py">day</span><span class="o">.</span><span class="py">toInt</span><span class="o">)</span>
<span class="k">val</span> <span class="nv">endDate</span> <span class="k">=</span> <span class="nv">startDate</span><span class="o">.</span><span class="py">plusDays</span><span class="o">(</span><span class="mi">1</span><span class="o">)</span>

<span class="k">val</span> <span class="nv">thirdPartyEvents</span> <span class="k">=</span> <span class="nf">getThirdPartyEvents</span><span class="o">(</span><span class="n">thirdPartyEventTable</span><span class="o">,</span> <span class="n">startDate</span><span class="o">,</span> <span class="n">endDate</span><span class="o">)</span>

<span class="nf">processThirdPartyEvents</span><span class="o">(</span><span class="n">entities</span><span class="o">,</span> <span class="n">subEntities</span><span class="o">,</span> <span class="n">entityEvents</span><span class="o">,</span> <span class="n">thirdPartyEvents</span><span class="o">)</span>
</code></pre></div></div>

<p>What I don’t like about this declaration is that I have to provide <code class="language-plaintext highlighter-rouge">entityEvents</code> and <code class="language-plaintext highlighter-rouge">thirdPartyEvents</code> as arguments in order to test <code class="language-plaintext highlighter-rouge">processThirdPartyEvents</code>. I also don’t like that the caller needs to know about those events. Had I known more about mocking <code class="language-plaintext highlighter-rouge">spark.read</code>, I would have called <code class="language-plaintext highlighter-rouge">spark.read</code> directly inside <code class="language-plaintext highlighter-rouge">processThirdPartyEvents</code> instead of requiring those events as parameters. I don’t want to make this function public solely for testing purposes; I wanted to hide it and test it as a black box.</p>

<p>I much prefer this declaration because it hides which events are used, so the caller doesn’t need to be concerned with them:</p>

<div class="language-scala highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">processThirdPartyEvents</span><span class="o">(</span><span class="n">entities</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">,</span> <span class="n">subEntities</span><span class="k">:</span> <span class="kt">DataFrame</span><span class="o">)</span>
</code></pre></div></div>

<p>So my advice is: learn more about the tools you use!</p>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="spark" /><category term="testing" /><category term="scala" /><summary type="html"><![CDATA[I was working with Scala Spark applications recently. Because I didn’t know much about mocking table reads in Spark and had to write a unit test for a function, I removed all the spark.read calls from the function. When I looked at the function to be tested, I noticed that it had become less cohesive.]]></summary></entry><entry><title type="html">My experience with data backfilling and achieving reproducibility</title><link href="https://nurgasemetey.com/my-experience-with-data-backfilling-and-achieving-reproducibility/" rel="alternate" type="text/html" title="My experience with data backfilling and achieving reproducibility" /><published>2024-09-15T03:00:00+03:00</published><updated>2024-09-15T03:00:00+03:00</updated><id>https://nurgasemetey.com/my-experience-with-data-backfilling-and-achieving-reproducibility</id><content type="html" xml:base="https://nurgasemetey.com/my-experience-with-data-backfilling-and-achieving-reproducibility/"><![CDATA[<p>Recently, I was busy with some data engineering related things and I want to share some insights.</p>

<h1 id="make-jobs-resilient-to-failure">Make jobs resilient to failure</h1>

<p>So you wrote following job that runs daily</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">item_events</span>
<span class="k">WHERE</span> <span class="n">item_events</span><span class="p">.</span><span class="n">item_id</span> <span class="k">in</span> <span class="p">(</span>
    <span class="k">SELECT</span> <span class="n">id</span>
    <span class="k">FROM</span> <span class="n">item</span>
    <span class="k">WHERE</span> <span class="n">item</span><span class="p">.</span><span class="n">status</span><span class="o">=</span><span class="s1">'ACTIVE'</span>
<span class="p">)</span> <span class="k">AND</span> <span class="n">item_events</span><span class="p">.</span><span class="nb">date</span> <span class="o">=</span> <span class="p">{</span><span class="nb">DATE</span><span class="p">}</span>
</code></pre></div></div>

<p>and deployed to production using your workflow management tool(ex. Airflow). Some time later you notice that logic is wrong and have to you run job again for last month.</p>

<p>The thing is that can go wrong is that you depend on <code class="language-plaintext highlighter-rouge">item.status</code>. Some items can have different statuses right now than month ago. On job rerun, events for items that were active in the previous month but are no longer active will not be saved.</p>

<p>You must remember that jobs will <strong>always</strong> fail for some reason. What is important is that how can you achieve correctness of backfilled data.</p>

<p>There are ways to solve this.</p>

<p><strong>Take a snapshot of tables</strong></p>

<p>You can take snapshots of <code class="language-plaintext highlighter-rouge">item</code> table for each day.</p>

<p>Example of snapshot data</p>
<pre><code class="language-csv">id,name,status,snapshot_date
1,itemA,ACTIVE,2024-09-13
1,itemA,ACTIVE,2024-09-14
1,itemA,PASSIVE,2024-09-15
</code></pre>

<p>New  job</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">item_events</span>
<span class="k">WHERE</span> <span class="n">item_events</span><span class="p">.</span><span class="n">item_id</span> <span class="k">in</span> <span class="p">(</span>
    <span class="k">SELECT</span> <span class="n">id</span>
    <span class="k">FROM</span> <span class="n">item_snapshot</span>
    <span class="k">WHERE</span> <span class="n">item</span><span class="p">.</span><span class="n">status</span><span class="o">=</span><span class="s1">'ACTIVE'</span> <span class="k">AND</span> <span class="n">snapshot_date</span><span class="o">=</span><span class="p">{</span><span class="nb">DATE</span><span class="p">}</span>
<span class="p">)</span> <span class="k">AND</span> <span class="n">item_events</span><span class="p">.</span><span class="nb">date</span> <span class="o">=</span> <span class="p">{</span><span class="nb">DATE</span><span class="p">}</span>
</code></pre></div></div>
<p>As you see, for that old date, item’s <code class="language-plaintext highlighter-rouge">status</code> will still be <code class="language-plaintext highlighter-rouge">ACTIVE</code>. Thus, you can be sure that on the job rerun it will give same results.</p>

<p><strong>Introduce new values to make job reproducible</strong></p>

<p>You can introduce new fields that will be valid throughout interval: <code class="language-plaintext highlighter-rouge">active_start_date</code> and <code class="language-plaintext highlighter-rouge">active_end_date</code>.</p>

<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="o">*</span>
<span class="k">FROM</span> <span class="n">item_events</span>
<span class="k">WHERE</span> <span class="n">item_events</span><span class="p">.</span><span class="n">item_id</span> <span class="k">in</span> <span class="p">(</span>
    <span class="k">SELECT</span> <span class="n">id</span>
    <span class="k">FROM</span> <span class="n">item</span>
    <span class="k">WHERE</span> <span class="n">item</span><span class="p">.</span><span class="n">status</span><span class="o">=</span><span class="s1">'ACTIVE'</span> <span class="k">OR</span> <span class="p">(</span><span class="n">item</span><span class="p">.</span><span class="n">active_start_date</span><span class="o">&lt;=</span><span class="p">{</span><span class="nb">DATE</span><span class="p">}</span> <span class="k">AND</span> <span class="n">item</span><span class="p">.</span><span class="n">active_end_date</span><span class="o">&gt;=</span><span class="p">{</span><span class="nb">DATE</span><span class="p">})</span>
<span class="p">)</span> <span class="k">AND</span> <span class="n">item_events</span><span class="p">.</span><span class="nb">date</span> <span class="o">=</span> <span class="p">{</span><span class="nb">DATE</span><span class="p">}</span>
</code></pre></div></div>

<p>On the rerun, it will still give correct results because we did stabilize it by introducing dates where it should be taken into account.</p>

<p>Of course, you can develop custom scripts to backfill data. Downside of this is that your script can contain errors too. However, approaches above will allow to rerun job by only clicking to relevant days from your workflow management tool.</p>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="data-engineering" /><summary type="html"><![CDATA[Recently, I was busy with some data engineering related things and I want to share some insights.]]></summary></entry><entry><title type="html">Spring Data Redis Performance issues with Redis Clustered</title><link href="https://nurgasemetey.com/spring-data-redis-clustered-performance-issues/" rel="alternate" type="text/html" title="Spring Data Redis Performance issues with Redis Clustered" /><published>2024-02-01T03:00:00+03:00</published><updated>2024-02-01T03:00:00+03:00</updated><id>https://nurgasemetey.com/spring-data-redis-clustered-performance-issues</id><content type="html" xml:base="https://nurgasemetey.com/spring-data-redis-clustered-performance-issues/"><![CDATA[<h1 id="multiget-or-mget">MultiGet or MGET</h1>

<p>Issues started with <code class="language-plaintext highlighter-rouge">multiGet</code> or <code class="language-plaintext highlighter-rouge">MGET</code></p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">redisTemplate</span><span class="p">.</span><span class="nf">opsForValue</span><span class="p">().</span><span class="nf">multiGet</span><span class="p">(</span><span class="n">keys</span><span class="p">)</span>
</code></pre></div></div>
<p>I ran command above in Clustered Redis and I saw that there were several MGET commands executed in different threads. I expected there would be <strong>one</strong> MGET command.</p>

<p>Later, I learned that my keys were landing to different slots. That’s why Spring Data Redis was executing several commands for each key.</p>

<p><img src="/assets/img/spring-data-redis-clustered-performance-issues/image.png" alt="alt text" /></p>

<p>There was nothing to do with it.</p>

<h1 id="pipelining">Pipelining</h1>

<p>Second part of my work was I had several entities and for each entity I had to run several <code class="language-plaintext highlighter-rouge">GETBIT</code>s. So I thought I can use pipelining.</p>

<p>Pipelining is recommended when you want to improve performance of your Redis operations.
Because operations did not require to be serial, pipeline seemed perfect solution.
In pipeline, commands were sent in batch through Redis connection and we just have to wait for answers.</p>

<h1 id="unexpected-performance-with-pipelining">Unexpected performance with pipelining</h1>

<p>So I ran pipelining and was shocked to see following</p>

<p><img src="/assets/img/spring-data-redis-clustered-performance-issues/image-1.png" alt="alt text" /></p>

<p>These were huge numbers.</p>

<p>Suspecting that it was related with pipelining, I tried to ran <code class="language-plaintext highlighter-rouge">GETBIT</code>s in plain for loop and saw that something was happening with pipeline.</p>

<p><img src="/assets/img/spring-data-redis-clustered-performance-issues/image-2.png" alt="alt text" /></p>

<p>Of course, I could have executed those <code class="language-plaintext highlighter-rouge">GETBIT</code>s parallely using threads but I wanted to squeeze better numbers so I decided to try LUA script.</p>

<p><strong>The thing you should remember that all keys that are used in LUA script should have to be in same slot.</strong></p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">local</span> <span class="n">bit1</span> <span class="o">=</span> <span class="n">redis</span><span class="p">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"GETBIT"</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value1</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">bit2</span> <span class="o">=</span> <span class="n">redis</span><span class="p">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"GETBIT"</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value2</span><span class="p">)</span>
<span class="kd">local</span> <span class="n">bit3</span> <span class="o">=</span> <span class="n">redis</span><span class="p">.</span><span class="n">call</span><span class="p">(</span><span class="s2">"GETBIT"</span><span class="p">,</span> <span class="n">key</span><span class="p">,</span> <span class="n">value3</span><span class="p">)</span>
</code></pre></div></div>

<p>And I saw following, which was huge improvement.</p>

<p><img src="/assets/img/spring-data-redis-clustered-performance-issues/image-3.png" alt="alt text" /></p>

<p><code class="language-plaintext highlighter-rouge">EVALSHA</code> - Evaluate a script from the server’s cache by its SHA1 digest.</p>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="spring-data" /><category term="redis" /><category term="spring-data-redis" /><category term="performance" /><summary type="html"><![CDATA[MultiGet or MGET]]></summary></entry><entry><title type="html">What I lack in OS X after Linux</title><link href="https://nurgasemetey.com/what-i-lack-in-osx-after-linux/" rel="alternate" type="text/html" title="What I lack in OS X after Linux" /><published>2022-02-11T03:00:00+03:00</published><updated>2022-02-11T03:00:00+03:00</updated><id>https://nurgasemetey.com/what-i-lack-in-osx-after-linux</id><content type="html" xml:base="https://nurgasemetey.com/what-i-lack-in-osx-after-linux/"><![CDATA[<p>As Linux user who had to switch to OS X because of work, I experienced considerable discomfort while switching from Linux.</p>

<h1 id="window-management">Window management</h1>

<h2 id="linux">Linux</h2>

<p>In Linux Mint Cinnamon at least, I would easily tile windows using keyboard shortcuts <code class="language-plaintext highlighter-rouge">Super</code>+<code class="language-plaintext highlighter-rouge">Left</code>, <code class="language-plaintext highlighter-rouge">Super</code>+<code class="language-plaintext highlighter-rouge">Right</code>, <code class="language-plaintext highlighter-rouge">Super</code>+<code class="language-plaintext highlighter-rouge">Left</code>, <code class="language-plaintext highlighter-rouge">Super</code>+<code class="language-plaintext highlighter-rouge">Up</code> in same workspace.</p>

<p>I would easily move window to another workspace again using keyboard shortcuts.</p>

<p>Also using <a href="https://www.nongnu.org/devilspie2/" title="Devilspie2">Devilspie2</a>, I could limit apps to certain workspaces.</p>

<h2 id="os-x">OS X</h2>

<p>Currently, I feel pain because I need to use trackpad and drag windows to another workspace. Although <a href="https://github.com/rxhanson/Rectangle" title="rxhanson/Rectangle: Move and resize windows on macOS with keyboard shortcuts and snap areas">Rectangle</a> somehow helps in tiling windows to some extent.</p>

<p>Another painful moment is maximize button behavior(it creates new workspace).</p>

<p>I constanly <code class="language-plaintext highlighter-rouge">alt-tabbing</code> to find my apps. No need to mention that there is no separate icon for multiple instances of same app. I use <a href="https://github.com/lwouis/alt-tab-macos">AltTab</a> which brings Windows and Linux alt-tab behavior.</p>

<h1 id="terminal-and-nano">Terminal and nano</h1>

<p>Terminal and Iterm 2 don’t support moving cursors word by word by default like in Linux using <code class="language-plaintext highlighter-rouge">Ctrl</code> + <code class="language-plaintext highlighter-rouge">Right</code> or <code class="language-plaintext highlighter-rouge">Ctrl</code>+<code class="language-plaintext highlighter-rouge">Left</code> . For that you need to go options and change settings.</p>

<p><code class="language-plaintext highlighter-rouge">nano</code> also does not support same behaviour.</p>

<h1 id="file-management">File management</h1>

<p>You can’t add some app to recommended list for opening files. It is especially needed when you want to open some <code class="language-plaintext highlighter-rouge">kt</code>, <code class="language-plaintext highlighter-rouge">js</code> or <code class="language-plaintext highlighter-rouge">.cpp</code> file using <code class="language-plaintext highlighter-rouge">Sublime Text</code>. OS X does not allow intuitive adding unless you must use <code class="language-plaintext highlighter-rouge">Automator</code> or go to app’s <code class="language-plaintext highlighter-rouge">plist</code> , edit it and run some command to link. As workaround, I open terminal and run <code class="language-plaintext highlighter-rouge">cat</code> command to see contents of file.</p>

<h1 id="launchers">Launchers</h1>

<p>Yes, it has Alfred or Raycast but I don’t want to buy Alfred and be locked to it. For example, Ulauncher is free and has much more plugins compared to Raycast.</p>

<p>Use cases</p>

<ul>
  <li>I can add current song in Spotify to favourites without going to app. I couldn’t find such behaviour in Raycast.</li>
  <li>I can copy to clipboard various timestamp formats. Again I couldn’t find such behaviour in Raycast.</li>
</ul>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="macos" /><category term="linux" /><summary type="html"><![CDATA[As Linux user who had to switch to OS X because of work, I experienced considerable discomfort while switching from Linux.]]></summary></entry><entry><title type="html">How to search: Programmer Version</title><link href="https://nurgasemetey.com/how-to-search-programmer-version/" rel="alternate" type="text/html" title="How to search: Programmer Version" /><published>2021-07-16T03:00:00+03:00</published><updated>2021-07-16T03:00:00+03:00</updated><id>https://nurgasemetey.com/how-to-search-programmer-version</id><content type="html" xml:base="https://nurgasemetey.com/how-to-search-programmer-version/"><![CDATA[<h1 id="websites">Websites</h1>

<p>You already know this.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>site:stackoverflow.com "interface"
</code></pre></div></div>

<p>Or other site</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>site:somesite.com "interface"
</code></pre></div></div>

<h1 id="github">Github</h1>

<p>I generally try to use <code class="language-plaintext highlighter-rouge">path</code> parameter to narrow. For example, for Typescript - <code class="language-plaintext highlighter-rouge">path:*.ts</code>.</p>

<p>Sometimes some code snippets could be found in issues and discussions.</p>

<h1 id="gitter">Gitter</h1>

<p>Some libraries use <code class="language-plaintext highlighter-rouge">gitter</code>, where people share their code snippets.</p>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="search" /><summary type="html"><![CDATA[Some techniques which could help to find relevant code]]></summary></entry><entry><title type="html">Procrastination Log Template From Habit Now Book</title><link href="https://nurgasemetey.com/procrastination-log-template/" rel="alternate" type="text/html" title="Procrastination Log Template From Habit Now Book" /><published>2021-05-11T03:00:00+03:00</published><updated>2021-05-11T03:00:00+03:00</updated><id>https://nurgasemetey.com/procrastination-log-template</id><content type="html" xml:base="https://nurgasemetey.com/procrastination-log-template/"><![CDATA[<p>Download PDF template <a href="/assets/files/Procrastination%20Log%20Template%20-%20Sheet1.pdf">here</a></p>

<p>Make copy of template in Google Sheets <a href="https://docs.google.com/spreadsheets/d/1u3bpXzdkCxJRHGsCrX5SzkTW0o3fCvDpgqAHQ_ll3f0/copy">here</a></p>

<p>Same template with different link <a href="https://docs.google.com/spreadsheets/d/1u3bpXzdkCxJRHGsCrX5SzkTW0o3fCvDpgqAHQ_ll3f0/template/preview">here</a></p>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="productivity" /><category term="habit now" /><summary type="html"><![CDATA[Download PDF template here]]></summary></entry><entry><title type="html">How I wrote Ulauncher extension for most used timedate macros</title><link href="https://nurgasemetey.com/extension-ulauncher/" rel="alternate" type="text/html" title="How I wrote Ulauncher extension for most used timedate macros" /><published>2020-11-24T03:00:00+03:00</published><updated>2020-11-24T03:00:00+03:00</updated><id>https://nurgasemetey.com/extension-ulauncher</id><content type="html" xml:base="https://nurgasemetey.com/extension-ulauncher/"><![CDATA[<h1 id="why">Why?</h1>

<p>I wrote a lot. And in my writings I use timestamps, dates, times, time-ranges.</p>

<p>I use following datetime formats</p>
<ul>
  <li>2020-11-01</li>
  <li>15-18</li>
  <li>14:58</li>
</ul>

<p>For them I used custom bash scripts which were copying to clipboard the date formats I wanted. These scripts were manually added to Linux’s custom shortcuts.</p>

<p>Example of bash script:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nb">alias </span><span class="nv">setclip</span><span class="o">=</span><span class="s1">'xclip -selection c'</span>
<span class="nb">alias </span><span class="nv">getclip</span><span class="o">=</span><span class="s1">'xclip -selection clipboard -o'</span>
<span class="nb">printf</span> <span class="si">$(</span><span class="nb">date</span> +<span class="s2">"%Y-%m-%d"</span><span class="si">)</span> | setclip
</code></pre></div></div>

<p>Initially, I was really happy but after some time number of custom scripts increased. I had difficulties with remembering them.</p>

<p><img src="/assets/img/extension-ulauncher/ulauncher-extension-shortcuts.png" alt="chelsea" class="img-responsive" /></p>

<h1 id="how">How?</h1>

<p>I stumbled to <a href="https://ulauncher.io/">Ulauncher</a> and it is downed on me that I could create extension for these timedate scripts.</p>

<p>For people who don’t know, <a href="https://ulauncher.io/">Ulauncher</a> is application launcher to which many developers write many extensions.</p>

<p>Some examples:</p>
<ul>
  <li>emoji extension</li>
  <li>process murderer</li>
  <li>google translate</li>
  <li>spotify player</li>
</ul>

<p>Here is the tutorial <a href="http://docs.ulauncher.io/en/latest/extensions/tutorial.html">link</a> for extension development.</p>

<h1 id="about-my-extension">About my extension</h1>

<p>Ulauncher requires that extension must have <code class="language-plaintext highlighter-rouge">manifest.json</code> file to operate.</p>

<p>Example of <code class="language-plaintext highlighter-rouge">manifest.json</code></p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"required_api_version"</span><span class="p">:</span><span class="w"> </span><span class="s2">"^2.0.0"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Timestamp macros"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"description"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Copy to clipboard the most used timedate formats"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"developer_name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Nurgazy Nazhimidinov"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"icon"</span><span class="p">:</span><span class="w"> </span><span class="s2">"images/icon.png"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"options"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
    </span><span class="nl">"query_debounce"</span><span class="p">:</span><span class="w"> </span><span class="mf">0.05</span><span class="w">
  </span><span class="p">},</span><span class="w">
  </span><span class="nl">"preferences"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w">
    </span><span class="p">{</span><span class="w">
      </span><span class="nl">"id"</span><span class="p">:</span><span class="w"> </span><span class="s2">"time_kw"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"keyword"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Timemacros"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"default_value"</span><span class="p">:</span><span class="w"> </span><span class="s2">"tm"</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">]</span><span class="w">
</span><span class="p">}</span><span class="w">

</span></code></pre></div></div>

<p>It says that if you write keyword <strong>Timemacros</strong>, extension will be on launcher(<strong>WARNING</strong> not clicked).</p>

<p><img src="/assets/img/extension-ulauncher/ulauncher-extension-keyword.png" alt="chelsea" class="img-responsive" /></p>

<hr />

<p>if you write <strong>Timemacros</strong> and click</p>

<p>or</p>

<p>write <strong>tm</strong> and <code class="language-plaintext highlighter-rouge">SPACE</code>, then <code class="language-plaintext highlighter-rouge">KeywordQueryEventListener(EventListener)</code> will invoked.</p>

<p>Here is the code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">DemoExtension</span><span class="p">(</span><span class="n">Extension</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
        <span class="nb">super</span><span class="p">(</span><span class="n">DemoExtension</span><span class="p">,</span> <span class="bp">self</span><span class="p">).</span><span class="n">__init__</span><span class="p">()</span>
        <span class="bp">self</span><span class="p">.</span><span class="n">subscribe</span><span class="p">(</span><span class="n">KeywordQueryEvent</span><span class="p">,</span> <span class="n">KeywordQueryEventListener</span><span class="p">())</span>

<span class="k">class</span> <span class="nc">KeywordQueryEventListener</span><span class="p">(</span><span class="n">EventListener</span><span class="p">):</span>

    <span class="k">def</span> <span class="nf">on_event</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">event</span><span class="p">,</span> <span class="n">extension</span><span class="p">):</span>
        <span class="n">items</span> <span class="o">=</span> <span class="p">[]</span>
        <span class="n">logger</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="s">'preferences %s'</span> <span class="o">%</span> <span class="n">json</span><span class="p">.</span><span class="n">dumps</span><span class="p">(</span><span class="n">extension</span><span class="p">.</span><span class="n">preferences</span><span class="p">))</span>
        <span class="n">logger</span><span class="p">.</span><span class="n">info</span><span class="p">(</span><span class="n">event</span><span class="p">.</span><span class="n">get_keyword</span><span class="p">())</span> <span class="c1"># gives the keyword 'tm'
</span>        <span class="n">items</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">ExtensionResultItem</span><span class="p">(</span><span class="n">icon</span><span class="o">=</span><span class="s">'images/icon.png'</span><span class="p">,</span>
                                         <span class="n">name</span><span class="o">=</span><span class="s">'YYYY-MM-DD'</span><span class="p">,</span>
                                         <span class="n">description</span><span class="o">=</span><span class="s">'{0:%Y-%m-%d}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()),</span>
                                         <span class="n">on_enter</span><span class="o">=</span><span class="n">CopyToClipboardAction</span><span class="p">(</span><span class="s">'{0:%Y-%m-%d}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()))))</span>

        <span class="n">items</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">ExtensionResultItem</span><span class="p">(</span><span class="n">icon</span><span class="o">=</span><span class="s">'images/icon.png'</span><span class="p">,</span>
                                         <span class="n">name</span><span class="o">=</span><span class="s">'HH:mm'</span><span class="p">,</span>
                                         <span class="n">description</span><span class="o">=</span><span class="s">'{0:%H:%M}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()),</span>
                                         <span class="n">on_enter</span><span class="o">=</span><span class="n">CopyToClipboardAction</span><span class="p">(</span>
                                             <span class="s">'{0:%H:%M}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()))))</span>

        <span class="n">items</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">ExtensionResultItem</span><span class="p">(</span><span class="n">icon</span><span class="o">=</span><span class="s">'images/icon.png'</span><span class="p">,</span>
                                         <span class="n">name</span><span class="o">=</span><span class="s">'YYYY-MM-DD HH:mm'</span><span class="p">,</span>
                                         <span class="n">description</span><span class="o">=</span><span class="s">'{0:%Y-%m-%d %H:%M}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()),</span>
                                         <span class="n">on_enter</span><span class="o">=</span><span class="n">CopyToClipboardAction</span><span class="p">(</span>
                                             <span class="s">'{0:%Y-%m-%d %H:%M}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()))))</span>
            
        <span class="k">return</span> <span class="n">RenderResultListAction</span><span class="p">(</span><span class="n">items</span><span class="p">)</span>


<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
    <span class="n">DemoExtension</span><span class="p">().</span><span class="n">run</span><span class="p">()</span>

</code></pre></div></div>

<p>As you can see <code class="language-plaintext highlighter-rouge">KeywordQueryEventListener(EventListener)</code> and <code class="language-plaintext highlighter-rouge">on_event</code> is run and <code class="language-plaintext highlighter-rouge">RenderResultListAction(items)</code> is returned and presented to user.</p>

<p>Our <code class="language-plaintext highlighter-rouge">items</code> would be</p>
<ul>
  <li>YYYY-MM-DD</li>
  <li>HH:mm</li>
  <li>YYYY-MM-DD HH:mm</li>
</ul>

<p>Here is the screenshot of items:</p>

<p><img src="/assets/img/extension-ulauncher/ulauncher-extension-items.png" alt="chelsea" class="img-responsive" /></p>

<p>Code for item:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">items</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">ExtensionResultItem</span><span class="p">(</span><span class="n">icon</span><span class="o">=</span><span class="s">'images/icon.png'</span><span class="p">,</span>
                                         <span class="n">name</span><span class="o">=</span><span class="s">'YYYY-MM-DD'</span><span class="p">,</span>
                                         <span class="n">description</span><span class="o">=</span><span class="s">'{0:%Y-%m-%d}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()),</span>
                                         <span class="n">on_enter</span><span class="o">=</span><span class="n">CopyToClipboardAction</span><span class="p">(</span><span class="s">'{0:%Y-%m-%d}'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">datetime</span><span class="p">.</span><span class="n">datetime</span><span class="p">.</span><span class="n">now</span><span class="p">()))))</span>

</code></pre></div></div>

<p>Explanation for <code class="language-plaintext highlighter-rouge">ExtensionResultItem</code>:</p>

<p><code class="language-plaintext highlighter-rouge">name</code> - title shown to user, look at screenshot above</p>

<p><code class="language-plaintext highlighter-rouge">description</code> - subtext which is also shown to user, look at screenshot above</p>

<p><code class="language-plaintext highlighter-rouge">on_enter</code> - most important part, says what to do if user clicks on this items. In my case, it is <code class="language-plaintext highlighter-rouge">CopyToClipboardAction</code> action which copies data to clipboard.</p>

<h1 id="summary">Summary</h1>

<p>At the end, I developed extension with enabled me to discard my custom bash scripts and forget about keyboard shortcuts for them.</p>

<p><a href="https://github.com/nurgasemetey/ulauncher-timestamp-macros">Source code on Github</a></p>

<p>Deprecated bash scripts 😄</p>

<p><img src="/assets/img/extension-ulauncher/ulauncher-extension-bash-scripts.png" alt="chelsea" class="img-responsive" /></p>

<hr />

<blockquote>
  <p><a href="https://dev.to/nurgasemetey/how-i-wrote-ulauncher-extension-for-most-used-timedate-macros-c5i">Article on dev.to</a></p>

  <p><a href="https://nurgasemetey.medium.com/how-i-wrote-ulauncher-extension-for-most-used-timedate-macros-f6ad14ee0bad">Article on Medium</a></p>
</blockquote>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="tool" /><summary type="html"><![CDATA[Ulauncher extension ...]]></summary></entry><entry><title type="html">How I used Amazon SES with MJML to create email templates in my side-project</title><link href="https://nurgasemetey.com/amazon-ses-mjml-email-template/" rel="alternate" type="text/html" title="How I used Amazon SES with MJML to create email templates in my side-project" /><published>2020-11-06T03:00:00+03:00</published><updated>2020-11-06T03:00:00+03:00</updated><id>https://nurgasemetey.com/amazon-ses-mjml-email-template</id><content type="html" xml:base="https://nurgasemetey.com/amazon-ses-mjml-email-template/"><![CDATA[<h1 id="how-it-started">How it started</h1>

<p>I wanted to send emails to my users on <a href="https://weekhabit.paraboly.com">https://weekhabit.paraboly.com</a>.</p>

<h1 id="first-attempt-mail-sending-services">First attempt: mail sending services</h1>

<p>Of course, I started with mail sending services like SendGrid, Mailchimp, SendInBlue. They are awesome services but I faced with <strong>various difficulties</strong>:</p>
<ul>
  <li>one was asking to buy a block of emails while I wanted to fly in free limits because user base is small now.</li>
  <li>in one I couldn’t even register</li>
  <li>one had small UX error where I couldn’t test my mail template because I removed sender but I couldn’t understand it from error.</li>
</ul>

<h1 id="another-attempt-amazon-ses">Another attempt: Amazon SES</h1>

<p>For introduction, Amazon SES</p>
<ul>
  <li>is email service, supporting sending emails from CLI and SDKs.</li>
  <li>doesn’t have template editor to create template. All templates must be created by users.</li>
</ul>

<p>As any programmer, I wanted to code this mail sending. 😀</p>

<p>Here is ther overview of CLI(AWS CLI must be installed and made login) commands for SES(more information can found in SES docs)</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws ses list-templates
aws ses get-template <span class="nt">--template-name</span> simple-template
aws ses create-template <span class="nt">--cli-input-json</span> file://simple-template.json
aws ses update-template <span class="nt">--cli-input-json</span> file://simple-template.json
aws ses send-templated-email <span class="nt">--cli-input-json</span> file://simple-template-single-user.json
aws ses send-bulk-templated-email <span class="nt">--cli-input-json</span> file://simple-template-bulk-users.json
</code></pre></div></div>

<p>Example of <code class="language-plaintext highlighter-rouge">simple-template.json</code></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Template"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"TemplateName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"simple-template"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"SubjectPart"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Greetings, !"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"HtmlPart"</span><span class="p">:</span><span class="w"> </span><span class="s2">"&lt;h1&gt;Hello ,&lt;/h1&gt;&lt;p&gt;Your favorite animal is .&lt;/p&gt;"</span><span class="p">,</span><span class="w">
      </span><span class="nl">"TextPart"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Dear ,</span><span class="se">\r\n</span><span class="s2">Your favorite animal is ."</span><span class="w">
    </span><span class="p">}</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p>Example of <code class="language-plaintext highlighter-rouge">simple-template-single-user.json</code></p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="w">  </span><span class="p">{</span><span class="w">
    </span><span class="nl">"Source"</span><span class="p">:</span><span class="s2">"WeekHabit Team &lt;week-habit-team@paraboly.com&gt;"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Template"</span><span class="p">:</span><span class="w"> </span><span class="s2">"mjml"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"ConfigurationSetName"</span><span class="p">:</span><span class="w"> </span><span class="s2">"ConfigSet"</span><span class="p">,</span><span class="w">
    </span><span class="nl">"Destination"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
      </span><span class="nl">"ToAddresses"</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="w"> </span><span class="s2">"someuser@gmail.com"</span><span class="w">
      </span><span class="p">]</span><span class="w">
    </span><span class="p">},</span><span class="w">
    </span><span class="nl">"TemplateData"</span><span class="p">:</span><span class="w"> </span><span class="s2">"{ </span><span class="se">\"</span><span class="s2">name</span><span class="se">\"</span><span class="s2">:</span><span class="se">\"</span><span class="s2">Alejandro</span><span class="se">\"</span><span class="s2">, </span><span class="se">\"</span><span class="s2">favoriteanimal</span><span class="se">\"</span><span class="s2">: </span><span class="se">\"</span><span class="s2">alligator</span><span class="se">\"</span><span class="s2"> }"</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span></code></pre></div></div>

<p><strong>Note</strong>: <code class="language-plaintext highlighter-rouge">ConfigSet</code> must be created in Amazon SES console otherwise email will not be sent.</p>

<h2 id="problem">Problem</h2>

<p>Did you notice that <code class="language-plaintext highlighter-rouge">HtmlPart</code> is  very simple and more suitable for system emails where style is not important.</p>

<p>Also Amazon SES doesn’t have template editor, so how to create <strong>responsive and stylish</strong> email?(One of many advantages of email services)</p>

<h1 id="mjml---responsive-template-framework">mjml - responsive template framework</h1>

<p>The mjml is tool which allows create responsive email. It has nice editor where you can design email template.</p>

<p><img src="/assets/img/amazon-ses-mjml-email-template/mjml_editor.png" alt="mjml editor" /></p>

<p>Standard workflow for generating email template html:</p>

<ol>
  <li>Create template in editor <a href="https://mjml.io/try-it-live">https://mjml.io/try-it-live</a></li>
  <li>Copy to file, let’s name <code class="language-plaintext highlighter-rouge">simple-template.mjml</code></li>
  <li>run(I assume that you installed <code class="language-plaintext highlighter-rouge">mjml</code>)
    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./node_modules/.bin/mjml <span class="nt">-r</span> simple-template.mjml <span class="nt">-o</span> simple-template.html
</code></pre></div>    </div>
  </li>
  <li>Copy <code class="language-plaintext highlighter-rouge">new-template.html</code> to <code class="language-plaintext highlighter-rouge">HtmlPart</code> part of <code class="language-plaintext highlighter-rouge">simple-template.json</code> described above.</li>
  <li>Update <code class="language-plaintext highlighter-rouge">template</code> by running</li>
</ol>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>aws ses update-template <span class="nt">--cli-input-json</span> file://simple-template.json
</code></pre></div></div>

<p><strong>Example</strong>:</p>

<p><img src="/assets/img/amazon-ses-mjml-email-template/mjml_example.png" alt="mjml editor" /></p>

<hr />

<blockquote>
  <p>Article on <a href="https://dev.to/nurgasemetey/how-i-used-amazon-ses-with-mjml-to-create-email-templates-in-my-side-project-3oi9">dev.to</a></p>

  <p>Article on <a href="https://nurgasemetey.medium.com/how-i-used-amazon-ses-with-mjml-to-create-email-templates-in-my-side-project-8118c88ae047">Medium</a></p>
</blockquote>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="amazon-ses" /><category term="mjml" /><category term="template" /><summary type="html"><![CDATA[Short story about Amazon SES]]></summary></entry><entry><title type="html">Express-jwt and Keycloak: how I did not use official Keycloak library</title><link href="https://nurgasemetey.com/nodejs-express-jwt/" rel="alternate" type="text/html" title="Express-jwt and Keycloak: how I did not use official Keycloak library" /><published>2020-11-01T03:00:00+03:00</published><updated>2020-11-01T03:00:00+03:00</updated><id>https://nurgasemetey.com/nodejs-express-jwt</id><content type="html" xml:base="https://nurgasemetey.com/nodejs-express-jwt/"><![CDATA[<h1 id="problem">Problem</h1>

<p>We have many microservices that run on multiple deployments. I wanted to add security by using Keycloak with the help of JWT.</p>

<h1 id="solution">Solution</h1>

<p>One of the earliest solution was to use <a href="https://github.com/keycloak/keycloak-nodejs-connect">Keycloak Js Adapter</a>. Yet, Keycloak JS adapter requires following:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">keycloakConfig</span> <span class="o">=</span> <span class="p">{</span>
    <span class="na">clientId</span><span class="p">:</span> <span class="dl">'</span><span class="s1">nodejs-microservice</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">bearerOnly</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">serverUrl</span><span class="p">:</span> <span class="dl">'</span><span class="s1">http://localhost:8080/auth</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">realm</span><span class="p">:</span> <span class="dl">'</span><span class="s1">Demo-Realm</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">credentials</span><span class="p">:</span> <span class="p">{</span>
        <span class="na">secret</span><span class="p">:</span> <span class="dl">'</span><span class="s1">62c99f7c-da55-48fb-ae4e-a27f132546b7</span><span class="dl">'</span>
    <span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>which seems cumbersome way of doing this.</p>

<hr />

<p>I thought there must be more simple way, I just wanted to <strong>validate requests</strong>.</p>

<p>That’s why I liked <a href="https://www.appsdeveloperblog.com/oauth2-resource-server-and-keycloak/">Spring Boot approach</a> which is:</p>
<ul>
  <li>include package</li>
</ul>

<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;dependency&gt;</span>
    <span class="nt">&lt;groupId&gt;</span>org.springframework.boot<span class="nt">&lt;/groupId&gt;</span>
    <span class="nt">&lt;artifactId&gt;</span>spring-boot-starter-oauth2-resource-server<span class="nt">&lt;/artifactId&gt;</span>
<span class="nt">&lt;/dependency&gt;</span>
</code></pre></div></div>

<ul>
  <li>add one line config</li>
</ul>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="s">spring.security.oauth2.resourceserver.jwt.issuer-uri = http://localhost:8080/auth/realms/appsdeveloperblog</span>
</code></pre></div></div>

<p>At start, it fetches makes request to <code class="language-plaintext highlighter-rouge">issuer-uri</code> which has response like this</p>

<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span><span class="w">
  </span><span class="nl">"realm"</span><span class="p">:</span><span class="w"> </span><span class="s2">"appsdeveloperblog"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"public_key"</span><span class="p">:</span><span class="w"> </span><span class="s2">"..."</span><span class="p">,</span><span class="w">
  </span><span class="nl">"token-service"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http://localhost:8080/auth/appsdeveloperblog/master/protocol/openid-connect"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"account-service"</span><span class="p">:</span><span class="w"> </span><span class="s2">"http://localhost:8080/realms/appsdeveloperblog/account"</span><span class="p">,</span><span class="w">
  </span><span class="nl">"tokens-not-before"</span><span class="p">:</span><span class="w"> </span><span class="mi">0</span><span class="w">
</span><span class="p">}</span><span class="w">
</span></code></pre></div></div>
<p>and stores <code class="language-plaintext highlighter-rouge">public_key</code> which is used to <strong>validate JWT tokens</strong>. It doesn’t make request each time to verify JWT.
As result, any request is validated and working out of box.</p>

<hr />

<p>So I wanted to replicate this on NodeJS.</p>

<p>I started with <a href="https://github.com/auth0/express-jwt">express-jwt</a> and simple example was like this</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">jwt</span> <span class="o">=</span> <span class="nx">require</span><span class="p">(</span><span class="dl">'</span><span class="s1">express-jwt</span><span class="dl">'</span><span class="p">);</span>

<span class="nx">app</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">/protected</span><span class="dl">'</span><span class="p">,</span>
  <span class="nx">jwt</span><span class="p">({</span> <span class="na">secret</span><span class="p">:</span> <span class="dl">'</span><span class="s1">shhhhhhared-secret</span><span class="dl">'</span><span class="p">,</span> <span class="na">algorithms</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">HS256</span><span class="dl">'</span><span class="p">]</span> <span class="p">}),</span>
  <span class="kd">function</span><span class="p">(</span><span class="nx">req</span><span class="p">,</span> <span class="nx">res</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">req</span><span class="p">.</span><span class="nx">user</span><span class="p">.</span><span class="nx">admin</span><span class="p">)</span> <span class="k">return</span> <span class="nx">res</span><span class="p">.</span><span class="nx">sendStatus</span><span class="p">(</span><span class="mi">401</span><span class="p">);</span>
    <span class="nx">res</span><span class="p">.</span><span class="nx">sendStatus</span><span class="p">(</span><span class="mi">200</span><span class="p">);</span>
  <span class="p">});</span>

<span class="c1">//Or with public key, shortened</span>

<span class="kd">var</span> <span class="nx">publicKey</span> <span class="o">=</span> <span class="nx">fs</span><span class="p">.</span><span class="nx">readFileSync</span><span class="p">(</span><span class="dl">'</span><span class="s1">/path/to/public.pub</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">jwt</span><span class="p">({</span> <span class="na">secret</span><span class="p">:</span> <span class="nx">publicKey</span><span class="p">,</span> <span class="na">algorithms</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">RS256</span><span class="dl">'</span><span class="p">]</span> <span class="p">});</span>

</code></pre></div></div>

<p>However it was problem for us to provide public key because</p>
<ul>
  <li>we have multiple deployments</li>
  <li>each deployment has its own Keycloak.</li>
</ul>

<p>We couldn’t maintain this so I decided to implement like in Spring Boot.</p>

<p>With the help <code class="language-plaintext highlighter-rouge">sync-request</code> package:</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">res</span> <span class="o">=</span> <span class="nx">request</span><span class="p">(</span><span class="dl">'</span><span class="s1">GET</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">http://localhost:8080/auth/realms/appsdeveloperblog</span><span class="dl">'</span><span class="p">);</span>
<span class="kd">const</span> <span class="nx">response</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">res</span><span class="p">.</span><span class="nx">getBody</span><span class="p">().</span><span class="nx">toString</span><span class="p">());</span>
<span class="kd">const</span> <span class="nx">publicKey</span> <span class="o">=</span> <span class="s2">`-----BEGIN PUBLIC KEY-----\r\n</span><span class="p">${</span><span class="nx">response</span><span class="p">.</span><span class="nx">public_key</span><span class="p">}</span><span class="s2">\r\n-----END PUBLIC KEY-----`</span><span class="p">;</span>

<span class="nx">app</span><span class="p">.</span><span class="nx">use</span><span class="p">(</span><span class="nx">jwt</span><span class="p">({</span> <span class="na">secret</span><span class="p">:</span> <span class="nx">publicKey</span><span class="p">,</span> <span class="na">algorithms</span><span class="p">:</span> <span class="p">[</span><span class="dl">'</span><span class="s1">RS256</span><span class="dl">'</span><span class="p">]</span> <span class="p">}));</span>
</code></pre></div></div>
<p>I achieved on-start fetch of public key without cumbersome settings on NodeJS.</p>

<hr />

<blockquote>
  <p><a href="https://dev.to/nurgasemetey/how-to-fetch-public-key-for-express-jwt-on-start-from-openid-on-nodejs-4hjc">Article on dev.to</a></p>

  <p><a href="https://nurgasemetey.medium.com/problem-728af2b5dbf3">Article on Medium</a></p>
</blockquote>]]></content><author><name>Nurgazy Nazhimidinov</name><email>nurgasemetey@gmail.com</email></author><category term="node" /><category term="springboot" /><category term="jwt" /><category term="keycloak" /><summary type="html"><![CDATA[I had task of adding security to microservices system using JWT...]]></summary></entry></feed>