<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="en">
	<title>Shiveen Pandita</title>
	<subtitle>I write about things that interest me in the moment</subtitle>
	<link href="https://shiveenp.com/feed/feed.xml" rel="self"/>
	<link href="https://shiveenp.com/"/>
	<updated>2026-02-15T03:59:31Z</updated>
	<id>https://shiveenp.com/</id>
	<author>
		<name>Shiveen Pandita</name>
		<email>blog@shiveenp.com</email>
	</author>
	
	<entry>
		<title>90% of software engineering is knowing what not to write</title>
		<link href="https://shiveenp.com/blog/90-of-software-engineering-is-knowing-what-not-to-write/"/>
		<updated>2026-02-15T03:59:31Z</updated>
		<id>https://shiveenp.com/blog/90-of-software-engineering-is-knowing-what-not-to-write/</id>
		<content type="html">&lt;p&gt;It&#39;s clear that software engineering is changing. Gone are the days of code output being the core bottleneck for most projects. In any medium to large company, the &lt;a href=&quot;https://medium.com/@addyosmani/my-llm-coding-workflow-going-into-2026-52fe1681325e&quot;&gt;plan-code-test&lt;/a&gt; cycle is the most efficient way to write software going forward. If you&#39;re still in denial, you&#39;re falling behind.&lt;/p&gt;
&lt;p&gt;However, that is not what defines a software engineer or engineering in general. The following is the first line defining engineering in &lt;a href=&quot;https://en.wikipedia.org/wiki/Engineering&quot;&gt;wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Engineering is the &lt;strong&gt;practice&lt;/strong&gt; of using natural science, mathematics, and the engineering design process to &lt;strong&gt;solve&lt;/strong&gt; problems within technology, increase efficiency and productivity, and improve systems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Notice how it consists of two broad active verbs, &lt;code&gt;practice&lt;/code&gt; and &lt;code&gt;solve&lt;/code&gt;. But there&#39;s a third word hiding in plain sight: &lt;strong&gt;design&lt;/strong&gt;. Design is inherently about tradeoffs — what to include &lt;em&gt;and&lt;/em&gt; what to exclude.&lt;/p&gt;
&lt;p&gt;We&#39;re seeing an inflection point in the solution part. Just like punch cards were made obsolete by handcrafting assembly, followed by ever higher level languages ever since; I expect the same thing will happen on a larger scale with LLMs. We&#39;re no longer constrained by code output anymore. And as these technologies improve, they will only allow for better integration and output patterns.&lt;/p&gt;
&lt;p&gt;So, what is left for an engineer to do? Ironically, a lot more. Pre-AI/Agentic coding, planning and coding constrained the delivery funnel for most &amp;quot;funded&amp;quot; engineering projects. With the coding part getting ever cheaper, you might think the funnel is now constrained by just planning.&lt;/p&gt;
&lt;p&gt;I believe you&#39;re wrong to assume that!&lt;/p&gt;
&lt;p&gt;See, the funnel didn&#39;t automatically get thinner in the middle just because code was inexpensive. In fact, the planning part of the funnel got wider and with higher stakes.&lt;/p&gt;
&lt;h2 id=&quot;inexpensive-code-means-harder-decisions&quot; tabindex=&quot;-1&quot;&gt;Inexpensive code means harder decisions &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/90-of-software-engineering-is-knowing-what-not-to-write/#inexpensive-code-means-harder-decisions&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When code is nearly free to produce, more things &lt;em&gt;can&lt;/em&gt; be built. And when more things can be built, there are more decisions about what should be built. The bottleneck shifts from &amp;quot;can we build this?&amp;quot; to &amp;quot;should we build this?&amp;quot;, which almost always is a much harder decision.&lt;/p&gt;
&lt;p&gt;The real value of an experienced engineer is increasingly in knowing what &lt;em&gt;not&lt;/em&gt; to write.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Knowing which features to say no to, even when they&#39;re easy to build. It&#39;s harder to reduce code than it is to add.&lt;/li&gt;
&lt;li&gt;Knowing when a third-party service or library is better than building in-house, including knowing when including it will cause debt which can only be known if you have the general tacit world knowledge only humans can possess&lt;/li&gt;
&lt;li&gt;Recognizing when a &amp;quot;simple&amp;quot; feature has hidden complexity — edge cases, operational burden, security surface area. LLMs are constrained by their context window, their model, and the narrow window of an execution decision tree they&#39;re operating in.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An AI-implemented solution leans heavily towards one path or the other in most circumstances. It will indiscriminately build a custom rate limiting solution and then suggest using redis for caching in another solution, not knowing that the problem domains are compatible across requests and sessions.&lt;/p&gt;
&lt;h2 id=&quot;creation-is-inexpensive-ownership-is-not&quot; tabindex=&quot;-1&quot;&gt;Creation is inexpensive, ownership is not &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/90-of-software-engineering-is-knowing-what-not-to-write/#creation-is-inexpensive-ownership-is-not&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Ownership is the crux of the difference between vibe/slop-coding apps and a real SaaS provider. Vibe coding begets vibe coding. As you don&#39;t develop the global context for your own project, a 2am page will be a nightmare for any complex app. Without understanding the internals of the system you&#39;re building, you will never realise that your picture-perfect custom auth solution is breaking because provider X did not implement the OAuth2 refresh tokenizing in the exact same way as all the other providers.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vibe coding begets vibe coding&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is where &amp;quot;knowing what not to write&amp;quot; becomes critical. The engineer who generates reams of code per day cannot truly compete with an engineer who glanced at the problem and said, yes, we should not roll our own auth.&lt;/p&gt;
&lt;p&gt;Most real customers, willing to pay you money, expect uptime, responsiveness, and a clean roadmap of features. This is not possible if your system grows like a jungle as you smash out another prompt. The only way this is possible is by knowing the real world constraints you&#39;re operating in and deeply understanding your codebase before opening your next claude code session or cursor tab.&lt;/p&gt;
&lt;h2 id=&quot;judgment-wins&quot; tabindex=&quot;-1&quot;&gt;Judgment wins &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/90-of-software-engineering-is-knowing-what-not-to-write/#judgment-wins&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;What separates senior engineers has always been judgment. But for a long time, that judgment was masked by the coding bottleneck. When writing code was slow and expensive, raw output was a reasonable proxy for contribution. Now that bottleneck is gone, and judgment is laid bare.&lt;/p&gt;
&lt;p&gt;Engineers who were primarily valued for code output are exposed. Those valued for architectural thinking and the ability to say &amp;quot;no&amp;quot; are more important than ever. The skill isn&#39;t typing faster or prompting better — it&#39;s pattern recognition built over years of seeing what works, what breaks, and what quietly becomes a liability.&lt;/p&gt;
&lt;h2 id=&quot;practice-makes-the-engineer&quot; tabindex=&quot;-1&quot;&gt;Practice makes the engineer &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/90-of-software-engineering-is-knowing-what-not-to-write/#practice-makes-the-engineer&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This brings us back to the engineering definition. The word &lt;strong&gt;practice&lt;/strong&gt; implies accumulated judgment, pattern recognition, and a deep understanding of what works and what doesn&#39;t. That part doesn&#39;t get automated away. It only amplifies with age and experience. An engineer with strong judgment and AI tools is dramatically more effective than either alone.&lt;/p&gt;
&lt;p&gt;This has been said in other online circles before, but the AI multiplication factor isn&#39;t linear. It sort of goes like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An engineer with poor pattern recognition and low world knowledge: Maybe 2x faster with coding agents and tools&lt;/li&gt;
&lt;li&gt;An engineer with years of honed pattern recognition and broad world knowledge: At least 10x faster with coding agents and tools&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The second lot is the engineer that can guide the company and the product to a sustainable cadence of delivering customer value, not marred by outages and tech debt.&lt;/p&gt;
&lt;p&gt;The future of software engineering isn&#39;t about writing more code. It&#39;s about writing less of it, more deliberately, with a clearer understanding of why each piece exists and what it costs to keep it alive.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>My Short-Lived Experiment with Readwise and Reader</title>
		<link href="https://shiveenp.com/blog/my-short-lived-experiment-with-readwise-and-reader/"/>
		<updated>2026-02-07T00:00:00Z</updated>
		<id>https://shiveenp.com/blog/my-short-lived-experiment-with-readwise-and-reader/</id>
		<content type="html">&lt;p&gt;Sometime around the end of 2025, I took stock of my reading habits. Over the years, I had started spending more time on social media (bad) and less time on curated feeds (bad, again). Where I usually would end my days reading or consuming feeds or saved articles, I now found myself endlessly doomscrolling.&lt;/p&gt;
&lt;p&gt;Considering one of my core goals of 2026 to restore balance back into my personal life, I decided to cut down on my social media consumption and fix how I consumed information about the world around me. My mainstays for the last few years had been Feedbin and Instapaper. Two fast and to the point apps, that simply do one job and do it well. Unfortunately, my queue in both of those had grown and covered the place like weeds.&lt;/p&gt;
&lt;p&gt;I also had been recently reading a lot more non-fiction and started toying with the idea of perhaps highlighting and revisiting passages more. Up until now, I was manually pulling the highlights from my kindle using calibre and putting them in Obsidian. It wasn&#39;t the end of the world, but it was definitely a chore. I looked into Readwise and learnt that they had branched out of their niche into a fully integrated read it later app that worked for RSS feeds, saved articles and newsletters.&lt;/p&gt;
&lt;p&gt;I wasn&#39;t a stranger to Readwise, having used it a few years back when all they offered was a Kindle sync to Roam  or Evernote. I toyed with it but eventually gave up as I didn&#39;t quite find as much value then. Ironically, this time I gave up for the opposite reason, but we will get to that in a minute.&lt;/p&gt;
&lt;p&gt;I exported out my feeds from Feedbin and saved articles from Instapaper and fed them to Readwise. I also connected my Kindle and Obsidian vault and had them linked and ready to go.&lt;/p&gt;
&lt;p&gt;Right off the bat, I was impressed with the feature set. Readwise has a ton of features (overwhelmingly so). The way the Reader app works is that it allows you to save articles and pull in your feeds. And then everyday it will pick a few and send them to your phone or email to review. It also has a neat &amp;quot;later&amp;quot; feature which allows you to defer reading things (but might still want to) for later. Think of it as your personal adaptive daily newspaper for the things you care about. Their mobile and web apps are elite and far better than any competition I had tried, and make the experience of using it enjoyable.&lt;/p&gt;
&lt;p&gt;On top of that, it&#39;s filled to the brim with features (these are the ones I interacted with, there might be a lot more):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Clean sorting out of new, quick and long reads - auto categorised.&lt;/li&gt;
&lt;li&gt;A Ghostreader feature that uses AI to fully read an article and summarise (also has audio transcription i think but I never tried)&lt;/li&gt;
&lt;li&gt;Ability to save pdfs and epubs and highlight them as well&lt;/li&gt;
&lt;li&gt;Great highlights and daily review feature, which is super useful addition from their other product and is now integrated in this product as well&lt;/li&gt;
&lt;li&gt;One underrated feature, at least for me, was the addition of a nice set of keyboard shortcuts. As a software engineer, I truly appreciate when someone spends time to add refined keyboard shortcuts to their app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, the novelty faded real quickly. After the first few weeks of opening the app with fervor every morning and reading the daily digest emails – I slowly started feeling disillusioned with the promise. The problem wasn&#39;t that the app did not provide me with the right information, rather it was that it provided too much of it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The problem wasn&#39;t that the app provided me with the right information, rather that it provided too much of it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;See, as much as I love reading other interesting peoples thoughts or random post-its I have collected from all over the internet, I realised I like reading them at my own pace. Readwise felt like it was a firehose aimed right at my eyeballs. Even worse, the constant vectors of notifications, push and email, with their various varieties was just stealing my attention. The reason I wanted to consolidate was to move away from social media and its constant need for attention. Instead, I ended up recreating it in Readwise.&lt;/p&gt;
&lt;p&gt;I know I could have turned off all the notifications, and that would be that - however, even after doing that, I felt the whole interface was also designed with the aforementioned goals in mind i.e., little UI elements to draw attention on the home page. That coupled with a few papercuts in regard to the reading experience (tiny images, bad HTML parsing etc.) I decided to end the experiment after a month.&lt;/p&gt;
&lt;p&gt;I am back to using Feedbin and Instapaper again. I missed the feel of both and how they stay out of my way and let me choose. I like that they don&#39;t clamour for attention and I also like in a weird way that they are not filled to the brim with features. I like nice, simple and boring software.&lt;/p&gt;
&lt;p&gt;Will I miss basic Readwise kindle highlights? unequivocally, yes. That said, I have also come to realise I care less about the highlights in general and anything important I want to note, I usually note it in the moment anyway. Sometimes restoring balance means choosing less over more.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Default Apps 2026</title>
		<link href="https://shiveenp.com/blog/default-apps-2026/"/>
		<updated>2026-01-29T11:25:29Z</updated>
		<id>https://shiveenp.com/blog/default-apps-2026/</id>
		<content type="html">&lt;ul&gt;
&lt;li&gt;📨 Mail Client: &lt;a href=&quot;https://gmail.com&quot;&gt;Gmail&lt;/a&gt; (web and iOS) with &lt;a href=&quot;https://improvmx.com&quot;&gt;ImprovMX&lt;/a&gt; for domain routing&lt;/li&gt;
&lt;li&gt;📮 Mail Server: &lt;a href=&quot;https://gmail.com&quot;&gt;Gmail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📝 Notes: &lt;a href=&quot;https://obsidian.md&quot;&gt;Obsidian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;✅ To-Do: &lt;a href=&quot;https://ticktick.com&quot;&gt;TickTick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📷 Phone Photo Shooting: Default iOS Camera App&lt;/li&gt;
&lt;li&gt;🟦 Photo Management: &lt;a href=&quot;https://lightroom.adobe.com&quot;&gt;Lightroom&lt;/a&gt; or &lt;a href=&quot;https://photos.google.com&quot;&gt;Google Photos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📆 Calendar: &lt;a href=&quot;https://flexibits.com/fantastical&quot;&gt;Fantastical&lt;/a&gt; with &lt;a href=&quot;https://calendar.google.com&quot;&gt;Google Calendar&lt;/a&gt; backend&lt;/li&gt;
&lt;li&gt;📁 Cloud File Storage: &lt;a href=&quot;https://drive.google.com&quot;&gt;Google Drive&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📖 RSS: &lt;a href=&quot;https://feedbin.com&quot;&gt;Feedbin&lt;/a&gt; (web + iOS)&lt;/li&gt;
&lt;li&gt;🙍🏻‍♂️ Contacts: &lt;a href=&quot;https://flexibits.com/cardhop&quot;&gt;Cardhop&lt;/a&gt; (only because it comes bundled with my fantastical sub), rarely opened&lt;/li&gt;
&lt;li&gt;🌐 Browser: &lt;a href=&quot;https://www.google.com/chrome/&quot;&gt;Google Chrome&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💬 Chat: &lt;a href=&quot;https://support.apple.com/messages&quot;&gt;iMessage&lt;/a&gt; + &lt;a href=&quot;https://www.whatsapp.com&quot;&gt;Whatsapp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔖 Bookmarks: &lt;a href=&quot;https://raindrop.io&quot;&gt;Raindrop.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📑 Read It Later: &lt;a href=&quot;https://www.instapaper.com&quot;&gt;Instapaper&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📜 Word Processing: &lt;a href=&quot;https://docs.google.com&quot;&gt;Google Docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📈 Spreadsheets: &lt;a href=&quot;https://sheets.google.com&quot;&gt;Google Sheets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;📊 Presentations: &lt;a href=&quot;https://slides.google.com&quot;&gt;Google Slides&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🛒 Shopping Lists: A frankenstein system made up of custom shortcuts + &lt;a href=&quot;https://support.apple.com/reminders&quot;&gt;Apple Reminders&lt;/a&gt; + &lt;a href=&quot;https://ticktick.com&quot;&gt;TickTick&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🍴 Meal Planning: &lt;a href=&quot;https://www.plantoeat.com&quot;&gt;PlanToEat&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;💰 Budgeting and Personal Finance: I don&#39;t budget ever (used to use &lt;a href=&quot;https://www.iggsoftware.com/banktivity/&quot;&gt;Banktivity&lt;/a&gt; in a previous life)&lt;/li&gt;
&lt;li&gt;📰 News: &lt;a href=&quot;https://news.google.com&quot;&gt;Google News&lt;/a&gt;, but I rarely read news.&lt;/li&gt;
&lt;li&gt;🎵 Music: &lt;a href=&quot;https://spotify.com&quot;&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🎤 Podcasts: I barely listen, if I do then it&#39;s &lt;a href=&quot;https://spotify.com&quot;&gt;Spotify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;🔐 Password Management: &lt;a href=&quot;https://1password.com&quot;&gt;1Password&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also see all the gear I use in &lt;a href=&quot;https://shiveenp.com/uses&quot;&gt;Uses page&lt;/a&gt;.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Lightroom CC to Google Photos</title>
		<link href="https://shiveenp.com/blog/lightroom-to-google-photos-batch-transfer/"/>
		<updated>2026-01-29T05:45:31Z</updated>
		<id>https://shiveenp.com/blog/lightroom-to-google-photos-batch-transfer/</id>
		<content type="html">&lt;p&gt;I use Lightroom for all my edits. I also use Google Photos for all my photo sharing and backups. Until a few months ago, I was using Lightroom Classic. My workflow then involved editing on my desktop, exporting the JPEGs manually, and pushing them to Google Photos.&lt;/p&gt;
&lt;p&gt;This was not arduous really, but it required me to keep my computer on for larger edits for the sync to fully go through.&lt;/p&gt;
&lt;p&gt;However, after moving to Lightroom CC, I discovered this is a lot easier and genuinely a great quality of life improvement over my previous workflow.&lt;/p&gt;
&lt;p&gt;Now, I can simply edit the photos and have them uploaded to the cloud. Then open Lightroom Web, and directly batch transfer to Google Photos. There are actually two share buttons, so you have to choose the right one. The classic share button (shown below) does not have the option.&lt;/p&gt;
&lt;img src=&quot;https://shiveenp.com/img/lightroom-non-google-share-button.jpeg&quot; alt=&quot;This button does not share&quot;&gt;
&lt;p&gt;The correct way is to &lt;code&gt;Cmd+A&lt;/code&gt; in your album or directory and then select share from the floating action bar that shows (highlighted below).&lt;/p&gt;
&lt;img src=&quot;https://shiveenp.com/img/lightroom-google-share-button.jpeg&quot; alt=&quot;This button does share&quot;&gt;
&lt;p&gt;This makes the process so much smoother than before. My photos are now batch uploaded in the background and available when done without me needing to keep tabs on the operation. This is another solid reason to move to the cloud version of Lightroom.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Four Computers</title>
		<link href="https://shiveenp.com/blog/four-computers/"/>
		<updated>2025-10-25T23:00:00Z</updated>
		<id>https://shiveenp.com/blog/four-computers/</id>
		<content type="html">&lt;p&gt;I currently have 4 different tiers of computing devices, listed in no particular preference order:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An iPhone 16 Pro&lt;/li&gt;
&lt;li&gt;An iPad Pro 11&amp;quot;&lt;/li&gt;
&lt;li&gt;An iPad Mini&lt;/li&gt;
&lt;li&gt;A Macbook Pro 14&amp;quot; M4 Pro&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On the outset, it seems like too many devices. And in all fairness, it probably is. But I get enough use out of all of them that I find them worth it.&lt;/p&gt;
&lt;p&gt;Here&#39;s how I think about each one.&lt;/p&gt;
&lt;h2 id=&quot;iphone&quot; tabindex=&quot;-1&quot;&gt;iPhone &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/four-computers/#iphone&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is my main device and is almost always on or near me. I use it for the basics: calling and texting people (duh), quick media consumption, some social media (I&#39;m not big on socials anymore), and taking photos.&lt;/p&gt;
&lt;p&gt;I don&#39;t play games on it, and I don&#39;t do any intensive reading either. I realized a few years back that too much time on the small screen just makes me grumpy. Maybe it&#39;s the text size, or maybe it&#39;s how I hunch like a gremlin when I use it. Either way, if something takes more than 10 minutes to consume, this isn&#39;t the device for it. That honour belongs to my beloved iPad Mini.&lt;/p&gt;
&lt;h2 id=&quot;ipad-mini&quot; tabindex=&quot;-1&quot;&gt;iPad Mini &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/four-computers/#ipad-mini&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I can&#39;t overstate how much I love this device. I was hesitant to get it at first—wasn&#39;t sure I needed another i(something) Apple thingamajig—but boy was I wrong. This device has surpassed all my expectations.&lt;/p&gt;
&lt;p&gt;This is my main &amp;quot;put the phone down&amp;quot; device, other than my e-reader. I use it for basically everything. Reading news? Tick. Booking flights? Tick. Consuming newsletters? Tick. Watching YouTube? Tick. Watching TV shows while the family uses the bigger screen? Tick. I could go on.&lt;/p&gt;
&lt;p&gt;It&#39;s the perfect device for casual consumption and surprisingly great for research too. I come home, put my phone away, and use the Mini for everything. I even got the cellular version, so sometimes I&#39;ll leave my phone at home if I just want to read and chill somewhere without it burning a hole in my pocket. This is also my go-to travel device.&lt;/p&gt;
&lt;p&gt;I just really wish it had a better screen with a higher refresh rate, but that&#39;s a minor gripe.&lt;/p&gt;
&lt;h2 id=&quot;ipad-pro&quot; tabindex=&quot;-1&quot;&gt;iPad Pro &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/four-computers/#ipad-pro&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the oldest device in the lineup. I bought it in 2021—it still has the M1 chip and works flawlessly for my needs. It stutters a bit more than it used to, but nothing that&#39;s enticed me to upgrade. Speaks volumes about how game-changing the M series hardware from Apple is.&lt;/p&gt;
&lt;p&gt;I use this sometimes as a consumption device, but truly this is my &amp;quot;get my life in order and sometimes write&amp;quot; device. I&#39;m writing this post right now, with a cup of espresso on the side, lounging on my deck with this iPad. I use it with the standard Apple keyboard, and it&#39;s a joy to write in this form factor. I love that the small screen keeps me focused.&lt;/p&gt;
&lt;p&gt;I also do any technical or long-form reading on this device—research papers, lengthy articles, that sort of thing. Calendar scheduling and todo management happen here too. The multitasking shines with split screen working perfectly for these workflows.&lt;/p&gt;
&lt;h2 id=&quot;macbook-pro&quot; tabindex=&quot;-1&quot;&gt;MacBook Pro &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/four-computers/#macbook-pro&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The workhorse of the family. I have the (almost) latest M4 Pro version, and it does everything I need. If there&#39;s one device here I would keep, it would be this. It handles basically any task thrown at it: photo editing in Lightroom, building apps, sending tax documents—all on this beast of a machine.&lt;/p&gt;
&lt;p&gt;I remember the Intel MacBooks getting so hot on my lap when doing anything intensive. The current MacBooks, though? Truly meant for lap use. I can do hours of intensive work without it ever feeling uncomfortable.&lt;/p&gt;
&lt;p&gt;For me, the MacBook is for anything the rest can&#39;t do—usually programming or graphics-intensive tasks. I also find some websites work better on the MacBook than the other devices, where the screen real estate gives me a better sense of what I need to do.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;So yes, four computers might seem excessive. But each one has carved out its own niche in my daily life, and together they make up a system that just works for how I live and work. Could I pare it down? Probably. Will I? Probably not.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>How I use AI</title>
		<link href="https://shiveenp.com/blog/how-i-use-ai/"/>
		<updated>2025-10-13T00:49:31Z</updated>
		<id>https://shiveenp.com/blog/how-i-use-ai/</id>
		<content type="html">&lt;p&gt;Let&#39;s get a few things straight first.&lt;/p&gt;
&lt;p&gt;I dislike AI.&lt;/p&gt;
&lt;p&gt;There are many rational reasons I can put forward on why, such as its a stochastic word box with an ability to override our judgment with its confident tone. It&#39;s a poker machine of banal ideas mixed in from a corpus of open source books and the internet, skimming the words but not understanding the reason. It has an eccentric bubble of overenthusiastic snake oil sellers that seem to associate with it. Last but not least, this part inherently more dangerous than anything else, is that it is indeed extremely useful in a subset of scenarios and initial conditions, which are hard to predict.&lt;/p&gt;
&lt;p&gt;Like any big technological or intellectual leap in human understanding or interfacing of the world, AI feels like a big jump from how we used to interact with the simple oracle like text box. We graduated from the simple HTML text &lt;code&gt;&amp;lt;input/&amp;gt;&lt;/code&gt; of Google search days to a sparkling text box that does much the same but with less degree of freedom on discernment.&lt;/p&gt;
&lt;p&gt;Alas, this post is not about my dislike.&lt;/p&gt;
&lt;p&gt;I did not want to be the only luddite left behind, perfectly encapsulating the grumpy old man wishing the halcyon old days of crunching reading 17 stackoverflow threads to get an answer to a simple question should come back. Rather, I wanted to embrace this new interfacing with the world knowledge.&lt;/p&gt;
&lt;p&gt;Committed now, I decided to buy a Claude pro subscription as my more AI-inclined tech friends reckoned it was the more elite highbrow option (I do not understand why, yet). After being a few hundreds of dollars poorer, I decided to put it through its paces so to speak and after a few months of using it, here are the things I use AI for.&lt;/p&gt;
&lt;h3 id=&quot;as-a-less-cumbersome-way-to-google-search&quot; tabindex=&quot;-1&quot;&gt;As a less cumbersome way to google search &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/how-i-use-ai/#as-a-less-cumbersome-way-to-google-search&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This for situations where I know I will follow the hivemind and don&#39;t care about confirmation bias.&lt;/p&gt;
&lt;p&gt;These are things like &amp;quot;what&#39;s a good butter brand in my local supermarkets?&amp;quot; or &amp;quot;What&#39;s the expected split for ETF investment for someone my age and country?&amp;quot; I find Claude&#39;s research mode extremely useful as it does offer vetted sources, places where I would have checked myself and more or less come to the same conclusion.&lt;/p&gt;
&lt;p&gt;I also use it as a first pass builder for things like travel itineraries or recipes. I like cooking but hate parsing through cookbooks for simple stuff. So sometimes I just ask Claude to give me basic steps for things like churning butter in our stand mixer.&lt;/p&gt;
&lt;p&gt;Admittedly, these are all tasks I could do myself, perhaps even better. But, as I said, my error margins for these asks or so high that even if I get put in the hallucinating answer part of the error graph, I will be okay.&lt;/p&gt;
&lt;h3 id=&quot;as-a-junior-dev-with-photographic-memory&quot; tabindex=&quot;-1&quot;&gt;As a junior dev with photographic memory &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/how-i-use-ai/#as-a-junior-dev-with-photographic-memory&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have used Claude code. I like Claude code. I love the way the text moves in my terminal and makes pretty little graphics while doing so and says silly little whimsical things like &amp;quot;Photosynthesising&amp;quot; or &amp;quot;Discombobulating&amp;quot;. But, here&#39;s the thing, it&#39;s too confident, and it does not think beyond a layer and half of the problem.&lt;/p&gt;
&lt;p&gt;Perhaps it&#39;s a prompting issue or in general imprecise instructions, but regardless, I am not baby sitting an &amp;quot;AI&amp;quot; to give me a perfect answer. I could build software before, and I can build software now—the only problem is that now, I also have to read while I write, which is not sustainable for me.&lt;/p&gt;
&lt;p&gt;So for me, I have settled on the following pattern of working with it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make it write tests after I have set up the patterns in the codebase. It still sometimes spits our absolute garbage, but saves me maybe 10 mins of typing worst case, which I still consider a win as I can fire this off async in between doing other things.&lt;/li&gt;
&lt;li&gt;Make it explain a class of bugs. There&#39;s a class of errors that are simple misconfigurations or too much or too little boilerplate. For this class, I ask the AI to try and fix it and I find that it generally works really well. For example, add the right config to connect to Postgres for this library in this YAML file.&lt;/li&gt;
&lt;li&gt;Finally, start any new tech. Recently, I have been experimenting with React native for some non-important work, and I get Claude to setup the project with best practices for me. These are usually beyond one-shot style tasks, so, I ask claude to setup a task plan and then get me to review it, before following the plan to do the thing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most people telling you that AI can do more than this, are perhaps a degree or two separation away from what real software really is or are prompt gods or have an ulterior motive, possibly all three combined.&lt;/p&gt;
&lt;h3 id=&quot;as-a-technical-assistant&quot; tabindex=&quot;-1&quot;&gt;As a technical assistant &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/how-i-use-ai/#as-a-technical-assistant&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the things I have recently started getting into is homelab&#39;ing. I have a setup at home with a BeeLink CPU and a QNAP NAS. I run portainer to manage containers which range from media management and handling to productivity software.&lt;/p&gt;
&lt;p&gt;One of the more cumbersome things of setting up a homelab is that you have to read the documentation of a dozen of different apps and docker containers, figuring out the right folder, networking, permission and metadata structure. This process can get extremely tedious.&lt;/p&gt;
&lt;p&gt;I use claude to do the dirty work for me. I heavily use the Claude projects feature here as it lets me compartmentalize the work, and I constantly feed it text and reference documents to keep track of.&lt;/p&gt;
&lt;p&gt;This works really well for me, most times. Sometimes it forgets the instructions I just gave it, such as using a particular group id for future docker compose files, or it hallucinates a docker image that just does not exist (and never did).&lt;/p&gt;
&lt;p&gt;But more or less it works really well, and I can smash out running services quickly whenever I feel like it without spending a whole evening reading getting started pages or blogs on how to do things.&lt;/p&gt;
&lt;p&gt;In the end, I think I get good use from the AI. I&#39;m still not convinced the pro sub was worth it, as there seem to be a few VC-funded AI apps out there including the unfortunately ubiquitous ChatGPT. However, it has given me enough value to not make me completely discount it.&lt;/p&gt;
&lt;p&gt;I think AI is a great technological leap if we separate the hype from the truth and use it for what it makes sense -- accelerating low-stakes mechanical pieces of work and using it as a way to lower the inertia to start more critical pieces of work.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>My Backbone Apps</title>
		<link href="https://shiveenp.com/blog/my-backbone-apps-2025/"/>
		<updated>2025-02-09T07:04:28Z</updated>
		<id>https://shiveenp.com/blog/my-backbone-apps-2025/</id>
		<content type="html">&lt;p&gt;It’s been a while since I have posted here, so today I wanted to share a bit about one of my favourite past time: productivity apps. Over the years, I have tried in some way or the almost all the popular (and sometimes not so popular apps). Through those years, I have gained an insight into, not only into the kind of apps that work with my particular bend of grey matter, but also the ones that are most likely to stick with me even if they don’t offer me the latest and greatest in features and UX.&lt;/p&gt;
&lt;p&gt;Does this mean they will stick with me forever? Probably not! I love redefining what personal and professional productivity means to me relatively often (but not as often as I used to) and that means there’s a chance that this list will be out of date in a year. However, as I’m getting older, I am less inclined to shake up my anchor digital systems, so who knows — this might be the end game for a while.&lt;/p&gt;
&lt;p&gt;Let’s dive into the system.&lt;/p&gt;
&lt;h2 id=&quot;things&quot; tabindex=&quot;-1&quot;&gt;Things &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/my-backbone-apps-2025/#things&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If there’s one app in my arsenal that has stood the test of time, jobs, personal milestones and innumerable other changes, that would be Things. This app is an indispensable tool and has been a constant companion for me almost 7 years. It’s the app that runs my life and is also the app, that I am most sensitive to missing features from when I switch to one of the other competitors. No matter how much the more shiny features in those other apps entice me, I always come back to Things, just because my brain works the fastest within the apps buttery smooth interface and strict bounds of structuring work.&lt;/p&gt;
&lt;p&gt;Over the years I have perfected my own system in Things, that works exactly like I want it to. It’s a system that I am hard pressed to replicate anywhere else other than the guilded cage of Things elegant GTD system and UX.&lt;/p&gt;
&lt;p&gt;If I had to pick just one app from this list as my forever productivity, it will unequivocally be Things.&lt;/p&gt;
&lt;figure class=&quot;w-full&quot;&gt;
&lt;img src=&quot;https://shiveenp.com/img/backbone-apps-things-screenshot.png&quot; alt=&quot;Current setup of Things&quot;&gt;
&lt;figcaption&gt;My current Things 3 Setup&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;obsidian&quot; tabindex=&quot;-1&quot;&gt;Obsidian &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/my-backbone-apps-2025/#obsidian&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Obsidian is the app where I come to think. Frankly, when I first tried Obsidian almost 2 years ago, I found the app ugly. However, almost a year ago, I was managing a lot of projects both personal and professional. Up until then I had been satisfied with Bear (especially the second version), which had been an mainstay app for managing my knowledge for a while - but, I seemed to keep hitting the limits of the app frequently. I realised I needed more degrees of freedom with my notes to allow lateral thinking and I decide to give Obsidian a go. To my surprise, not only had the app improved by leaps and bound, the sync, plugins and overall look and feel that could be customised to look quite nice now, made me really get back into it.&lt;/p&gt;
&lt;p&gt;I haven&#39;t looked back since. I don&#39;t follow any PARAs or a million other Obsidian guru&#39;s selling short courses on how to organise your &amp;quot;PKM&amp;quot;. I simply dump my notes, tag when I feel like, file them in folders when I feel like it. The paid sync service has been flawless for me and I do all my thinking, archiving and most importantly writing in it now (including this post).&lt;/p&gt;
&lt;p&gt;I still like Bear and I think the app has a lot of potential; but at this point of my personal and professional life, obsidian fits me better.&lt;/p&gt;
&lt;figure class=&quot;w-full&quot;&gt;
&lt;img src=&quot;https://shiveenp.com/img/backbone-apps-obsidian-screenshot.png&quot; alt=&quot;Current setup of Obsidian&quot;&gt;
&lt;figcaption&gt;A screenshot of my obsidian&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;fantastical&quot; tabindex=&quot;-1&quot;&gt;Fantastical &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/my-backbone-apps-2025/#fantastical&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The flip side to Things, Fantastical is the place where I commit to things. I have been a long time calendar user, getting super into it during my uni days. I have continued using calendars as immutable event ledger of my life since then. Every important commitment makes its way back into my calendar. I used and enjoyed Apple calendar for quite some time, until I finally pulled the plug and got Fantastical around 3 years ago and in that time it has become a core part of my daily workflow.&lt;/p&gt;
&lt;p&gt;I enjoy using the &lt;em&gt;fluid&lt;/em&gt; NLP to add events, switch between calendar sets using keyboard shortcuts, interesting calendars (i subscribe to the F1 calendar and Aussie holidays). I also love that it syncs with my work google workspace account, fastmail calendar and our shared family iCloud calendar in an instant and just always works. I can&#39;t count the number of times Apple calendar has missed an appointment request via google workspace, completely lost an event or just simply decided to not alert me for an event change.&lt;/p&gt;
&lt;p&gt;I don&#39;t subscribe to the school of tasks and events in the same app. I prefer to keep the separate. My tasks tell me what to do, but they&#39;re are more or less fungible. But my events are not. It&#39;s very rare that I put something in my calendar that is fungible.&lt;/p&gt;
&lt;figure class=&quot;w-full&quot;&gt;
&lt;img src=&quot;https://shiveenp.com/img/backbone-apps-fantastical-screenshot.png&quot; alt=&quot;Current setup of Fantastical (note I have the personal calendar set for obvious privacy and non disclosure reasons)&quot;&gt;
&lt;figcaption&gt;A screenshot of Fantastical (note I have the personal calendar set for obvious privacy and non disclosure reasons)&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2 id=&quot;devonthink&quot; tabindex=&quot;-1&quot;&gt;Devonthink &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/my-backbone-apps-2025/#devonthink&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This has been a blackhorse entry into my workflow over the last few months. Initially, I hesitated mentioning it in this post as I do think it sits in a separate island to the apps above. However, while the past apps are my golden trifecta; this is such an indispensable tool for me, that it&#39;s worth a mention.&lt;/p&gt;
&lt;p&gt;Devonthink is many things (far more than I probably will ever use it for), but I essentially use it as my &lt;em&gt;paperless office + research vault&lt;/em&gt;. I have a few databases for personal and financial documents. This is where anything that is saved to my desktop or scanner folder in iCloud gets auto moved to (thanks to Hazel and Devonthinks &lt;code&gt;ctrl+c&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I also use the companion Devonthink to Go app for iPhone and iPad; and it has already saved me from going home and fetching documents a few times. There are two main features of devonthink that are core to my workflow now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Auto moving files to the right database and folder, based on it learning my behaviours for previous files&lt;/li&gt;
&lt;li&gt;Being a really good pdf reader and highlighter/note taker on my iPad - which I use to read research papers and pdf ebooks&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s an expensive one time purchase app, but I find it&#39;s good value for me. Might not be for everyone.&lt;/p&gt;
&lt;figure class=&quot;w-full&quot;&gt;
&lt;img src=&quot;https://shiveenp.com/img/backbone-apps-devonthink-screenshot.png&quot; alt=&quot;Current setup of Devonthink&quot;&gt;
&lt;figcaption&gt;Current Devonthink setup with databases&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;hr&gt;
&lt;p&gt;Overall, super happy with this setup for now, as I find it help me stay productive; but also allows me the freedom to flex hidden or more advanced capabilities of these apps when the need calls for.&lt;/p&gt;
&lt;p&gt;I plan to do an individual in depth workflow posts for each of these apps in the near future, detailing my own workflow.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>The problem with VC model</title>
		<link href="https://shiveenp.com/blog/the-problem-with-vc-model/"/>
		<updated>2024-11-09T06:59:14Z</updated>
		<id>https://shiveenp.com/blog/the-problem-with-vc-model/</id>
		<content type="html">&lt;blockquote&gt;
&lt;p&gt;The core problem with VC funding model is that they want you to pay for the failures of their 10 other ventures.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have been thinking about this for a while and am writing down my thoughts from my lens.
Most VC-funded or run businesses are not primarily running to make the consumer&#39;s lives better in a sustainable way. It does happen, but it is a side effect rather than the core goal. Regular lending and or bootstrapping does not have this issue. This is the biggest problem for the majority of startups and companies these days. These sorts of companies have to raise a lot of money and entice people into their ecosystem with free or almost free offerings, hoping to lure in solo/team users. They also sweeten the pot with a pleasing user experience, excellent onboarding, and other little delighter features that make people stay with them.&lt;/p&gt;
&lt;p&gt;To be honest, I see nothing wrong with this approach in principle. It&#39;s a viable strategy to building a business. However, back in the &#39;olden days &#39;, which I would roughly define as the pre-VC era, most companies started with an ambition to keep it running sustainably as long as possible and weren&#39;t tainted with the VC money printing machine ideology. Or that&#39;s how I anecdotally feel the difference is.&lt;/p&gt;
&lt;p&gt;These days, most tech businesses start with the expectation to monetize as quickly as possible. This often leads them to fall into the trap of maximizing all the usual signalling metrics for sale, such as user count, retention, and media hype. Once a critical threshold of market and hype mindshare is reached, they pivot to some exorbitant usage fee, especially concerning their added value. Once the business reaches this point, two things happen:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Either the product is kept alive to generate more revenue till the VC overlords can then sell to another company or even different VC overlords and make the founders (and sometimes the founding team) a fuckload of money&lt;/li&gt;
&lt;li&gt;Or, the product plateaus and the team tries to find a way to extract as much money as possible till it changes hands to a private equity firm.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Personally, I find both of these approaches distasteful. The software and businesses I value the most are those that are value-driven, with a strong product-market fit and a desire to be in it for the long haul. They&#39;re not pressured to fail fast and fail hard, burning stacks of cash in the process to quickly build market share by adding tons of features. In my opinion, slow and steady wins the trust race and stands the test of time.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Balancing strategic vs tactical work in software engineering</title>
		<link href="https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/"/>
		<updated>2024-05-19T00:42:31Z</updated>
		<id>https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/</id>
		<content type="html">&lt;p&gt;I have been thinking about a sapling of an idea for a while now. It all started when I was helping my team at work with several important projects over the past few months. The basic idea I’ve been playing with is that in software engineering, there is a tension between things that are important in the short term and things that are important in the long term. Switching between these two levels of &lt;em&gt;mental context&lt;/em&gt; can be (or was for me) challenging. For the sake of simplicity, I call the work to be done for the longer-term “strategic” work and the work to be done for the short-term “tactical” work.&lt;/p&gt;
&lt;p&gt;ICs that have progressed far enough in their career are always making a tradeoff between these two abstract levels of work. This results &lt;strong&gt;in a single day&lt;/strong&gt; from dealing with strategic concerns such as product direction and architectural fitness to tactical concerns such as our deployment pipeline can’t handle integration tests with a new database. Consequently, one might be in a whirlwind of context shifts daily. There is a similar enough parallel here to the now famous PG Essay on Maker vs Manager Schedule &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;However, whilst it’s easy to know the difference between manager vs maker schedule and work around it, this might not always be the case between strategic and tactical work. For example, on a given heavy meetings day I might have a calendar that looks like this:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://shiveenp.com/img/example-packed-calendar.png&quot; alt=&quot;Image showing a packed day of meetings&quot;&gt;&lt;figcaption&gt;Busy meeting day&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;and I instantly knew today would be running on a manager’s schedule. Usually, I can anticipate well in advance (I say usually because sometimes things happen, like an incident at work, and the fallout can throw a spanner in everything) and plan around it. I can bunch up admin tasks in between the meetings or small coding tasks like updating a yml file to use a more efficient AWS instance in a region on prod — things that require minimal context loading in my brain and are on auto-pilot for me.&lt;/p&gt;
&lt;p&gt;Similarly, during my weekly review, I can identify my core “maker” schedule days, and depending on my top tasks for the day/week, I can schedule things to maximise context and focus.&lt;/p&gt;
&lt;p&gt;Now the problem arises within the context of these days and it doesn’t matter whether it’s on a manager or a maker day. I have to be switched on and go from “stratosphere to mantle” on different topics.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Easy to differentiate and control manager and maker days, hard to do so with strategic and tactical context of thinking&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There might be a meeting about our next quarter’s engineering planning where I need to zoom out and look at projects to deliver a coherent vision as Lego blocks. But then there might be a meeting right after that might pull me into the weeds of why the calls on a specific REST endpoint run slower when the same call manages a decent response latency when made via graphql.&lt;/p&gt;
&lt;p&gt;In the same way, on my maker days, I might have to think between coming up with answers for a system-wide integration of a new greenfield service and figuring out why the headers sent by service X are not being parsed by the service(s) we run and manage.&lt;/p&gt;
&lt;p&gt;Unfortunately, there are no concrete rules to follow (unlike maker vs manager schedule) to allow an easy transition between these two states. And I am trying to figure this out myself.&lt;/p&gt;
&lt;p&gt;That said, a few techniques have helped me here in the past. These include:&lt;/p&gt;
&lt;h2 id=&quot;context&quot; tabindex=&quot;-1&quot;&gt;Context &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/#context&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Have “context docs” running for everything. This is my top tip. You might be better than me and be able to hold a lot of context, but my brain is a bootleg NVRAM, and not much survives soft resets (a soft reset happens anytime when I have looked away from the problem for &amp;gt; 20 minutes) here. So, I keep context logs of everything so I can quickly return to them when necessary. It can be as simple as a piece of paper or as complicated as an obsidian database, whatever floats your boat.&lt;/p&gt;
&lt;h2 id=&quot;morning-vs-evening-blocks&quot; tabindex=&quot;-1&quot;&gt;Morning vs evening blocks &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/#morning-vs-evening-blocks&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Even within strategic and tactical work, it can be divided between doing discovery work vs known work. Discovery work involves figuring stuff out before you can put the approach or implementation on paper. Whereas known stuff is what you can derive from your existing knowledge. When possible, I keep any discovery-based strategic or tactical work in the morning vs keeping known work in the evening. I find mornings work better because I am open to more ideas and can spin my gears better. This might be different for you, so do what works. The core idea is to keep discovery-based S/T work at the time when you find most of your creative juices flowing. Planning is critical here.&lt;/p&gt;
&lt;h2 id=&quot;batch-batch-batch&quot; tabindex=&quot;-1&quot;&gt;Batch, batch, batch &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/#batch-batch-batch&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is an obvious one, but if possible, batch Strategic or Tactical work. This is not quickly done, but based on your specific role or job profile, batching work, similar to a maker vs. manager schedule, can reap huge benefits.&lt;/p&gt;
&lt;h2 id=&quot;acceptance&quot; tabindex=&quot;-1&quot;&gt;Acceptance &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/#acceptance&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Finally, but perhaps most importantly, be okay with being interrupted. I think the biggest flaw in over-optimising this flow (which I have done to myself a few times as well) is to over-index too hard on when you need to be doing strategy vs when you need to be deep in the code trenches. Sometimes, shit just happens and you gotta roll with it. Keep that perspective.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;https://paulgraham.com/makersschedule.html &lt;a href=&quot;https://shiveenp.com/blog/balancing-strategic-vs-tactical-work-in-engineering/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
	</entry>
	
	<entry>
		<title>Circuit Breakers For Your Life</title>
		<link href="https://shiveenp.com/blog/circuit-breakers-for-your-life/"/>
		<updated>2024-05-06T10:09:31Z</updated>
		<id>https://shiveenp.com/blog/circuit-breakers-for-your-life/</id>
		<content type="html">&lt;p&gt;In today’s fast-paced world, we often find ourselves juggling multiple tasks and responsibilities, leaving us feeling overwhelmed and exhausted. Just like electrical circuits have circuit breakers to prevent overloading, we too need a similar mechanism to prevent ourselves from being overwhelmed.&lt;/p&gt;
&lt;p&gt;Circuit breakers can be small or large, depending on the degree of overwhelmingness we are experiencing. For different people, these circuit breakers can take different forms. It could be something that tells us that we need to take a break, slow down, and prioritize our well-being.&lt;/p&gt;
&lt;p&gt;For me personally, it’s a combination of feedback from my wife and my own mental state that serves as a circuit breaker. If I find myself dreading going to work, or if my wife tells me that I’ve been looking down and stressed, I know it’s time to take a break and reassess my priorities. It could mean taking a few days off, planning a holiday, or simply slowing down.&lt;/p&gt;
&lt;p&gt;Just like electrical circuits, our lives can benefit from circuit breakers. They help us prevent burnout, keep us operating at peak capacity, and allow us to take stock of our priorities. However, it’s essential to use them wisely. Too many circuit breakers in quick succession may indicate that we need to reevaluate our current priorities and regain our focus.&lt;/p&gt;
&lt;p&gt;To end this, having circuit breakers in our lives is vital to prevent ourselves from being overwhelmed. It’s crucial to take breaks, slow down, and prioritize our well-being to operate at our best capacity. So, the next time you find yourself feeling overwhelmed, take a step back, and find your circuit breaker to recharge and regain your focus.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Fix Git Branch Prefix Uppercasing</title>
		<link href="https://shiveenp.com/blog/fix-git-branch-prefix-suddenly-uppercase/"/>
		<updated>2024-01-29T02:59:31Z</updated>
		<id>https://shiveenp.com/blog/fix-git-branch-prefix-suddenly-uppercase/</id>
		<content type="html">&lt;h2 id=&quot;context&quot; tabindex=&quot;-1&quot;&gt;Context &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/fix-git-branch-prefix-suddenly-uppercase/#context&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes git gets confused and uppercases the branch names. This happened to me today while I was working on a bug and
on git checkout &lt;code&gt;issue/*&lt;/code&gt; prefix kept getting renamed to &lt;code&gt;ISSUE/*&lt;/code&gt; and this usually breaks the auto build mechanism at work for me, since we have
it configured to look for a particular regex. This is not a particularly complex or even a unique problem, but I encounter it so infrequently (second time now),
that I thought its worth just writing about even if for my own reference.&lt;/p&gt;
&lt;h2 id=&quot;how-to-fix&quot; tabindex=&quot;-1&quot;&gt;How to fix &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/fix-git-branch-prefix-suddenly-uppercase/#how-to-fix&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;First step is to fix the refs. This is taken from this &lt;a href=&quot;https://stackoverflow.com/questions/15371866/why-is-git-capitalizing-my-branch-name-prefix&quot;&gt;thread&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; .git/refs/heads
&lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; ISSUE issue&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the command above should fix the issue ref (folder) name so new issues don’t have the uppercased prefix.&lt;/p&gt;
&lt;p&gt;Now, we tackle existing branches. If a branch has been created but not pushed do:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; branch &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;old_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;new_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My git complained that there was already a branch with an existing name when all i did was change the branch name from &lt;code&gt;ISSUE/existing-branch-name&lt;/code&gt; to &lt;code&gt;issue/existing-branch-name&lt;/code&gt; so I renamed it as &lt;code&gt;issue/existing-branch-name-2&lt;/code&gt; (same name but with a prefix).&lt;/p&gt;
&lt;p&gt;however, if a branch has been pushed, after the previous step do the following:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push origin &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;old_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push origin &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;new_name&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;this will fix up any broken branch names already pushed, whilst preserving the changes.&lt;/p&gt;
&lt;p&gt;That should fix it up nicely ✅&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Make Next DNS work with Arc Browser</title>
		<link href="https://shiveenp.com/blog/arc-make-nextdns-work/"/>
		<updated>2024-01-29T02:59:31Z</updated>
		<id>https://shiveenp.com/blog/arc-make-nextdns-work/</id>
		<content type="html">&lt;p&gt;I recently jumped on the bandwagon (again) for &lt;a href=&quot;https://arc.net/&quot;&gt;Arc&lt;/a&gt;, as I&#39;m really not feeling Chrome&#39;s changes to shovel more ads down
users throats.&lt;/p&gt;
&lt;p&gt;One of the little pieces of set and forget software that I really like is &lt;a href=&quot;https://nextdns.io/&quot;&gt;NextDNS&lt;/a&gt;. I have been using
them for coming up to 3 years now. The way I use it on my Mac is via profiles, which means it should be available
to any app that uses system dns. Chrome doesn&#39;t follow system dns but the setting is easily avaiable in preference. However,
I couldn&#39;t find anything that allowed me to change this setting readily in Arc, since their settings panel is kinda sparse. which resulted in:&lt;/p&gt;
&lt;img src=&quot;https://shiveenp.com/img/no-next-dns.png&quot; alt=&quot;Image showing NextDNS not configured for ARC&quot;&gt;
&lt;p&gt;However, you can override the macos system dns settings to make it work. Just run:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;defaults &lt;span class=&quot;token function&quot;&gt;write&lt;/span&gt; company.thebrowser.Browser BuiltInDnsClientEnabled &lt;span class=&quot;token parameter variable&quot;&gt;-boolean&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and just like that, my boi is back again&lt;/p&gt;
&lt;img src=&quot;https://shiveenp.com/img/yes-next-dns.png&quot; alt=&quot;Image showing NextDNS properly configured for ARC&quot;&gt;
</content>
	</entry>
	
	<entry>
		<title>Contributor vs Maintainer Mentality</title>
		<link href="https://shiveenp.com/blog/contributor-vs-maintainer-mentality/"/>
		<updated>2023-07-16T03:41:31Z</updated>
		<id>https://shiveenp.com/blog/contributor-vs-maintainer-mentality/</id>
		<content type="html">&lt;p&gt;One of the main things I have been chewing on over the last few weeks is the quality of technical decisions made by different people working on a shared codebase. The thoughts then led me to think about the contributor vs maintainer mentality.&lt;/p&gt;
&lt;p&gt;Although the terminology has been used extensively in the context of open source software (OSS) &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://shiveenp.com/blog/contributor-vs-maintainer-mentality/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; - the writing here is more focused in terms of proprietary software written in SaaS-based companies (both startup and enterprise). This is because, unlike OSS, where there are one or few people that own and guide the repo, in software-based companies, that&#39;s different. In these companies, you&#39;ll have a team or a set of teams owning a service domain, which could be powered by one more service. Hence, the distinction becomes more nuanced and less obvious than in OSS, where code repositories directly promote the code owner/maintainer vs contributor designations. This in turn, means that the distinction is more of a mentality than designations.&lt;/p&gt;
&lt;p&gt;One set of people has the &lt;strong&gt;contributor&lt;/strong&gt; mentality. They see their job as coming in and making changes for a well-defined end goal, i.e., adding more features, fixing bugs, and patching security vulnerabilities. This set of people, however, will only care about moving the needle forwards within their local concern. They might optimise the software just in time to a local maxima that serves their current need but rarely go the extra mile. However, this is not to say that this group of people have the wrong engineering motivations or can&#39;t be trusted to help evolve the code. More often than not, people with a contributor mindset are also the &amp;quot;&lt;em&gt;get shit done&lt;/em&gt;&amp;quot; types and view software as a means to an end.&lt;/p&gt;
&lt;p&gt;The other set of individuals fall in the camp of &lt;strong&gt;maintainer&lt;/strong&gt; mentality. These are the individuals who not only look at the codebase as a means to an end but instead as an evolving machine that needs to be curated and tended to routinely. For many companies, these are individuals in a tech lead or architect role (not always true). This particular set of people is always striving to improve the software&#39;s quality which benefits them and anyone else working in the system.&lt;/p&gt;
&lt;p&gt;It&#39;s hard to say which one is better. The chances of your software lasting over 3-5 years &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://shiveenp.com/blog/contributor-vs-maintainer-mentality/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; without a huge rewrite are slim. Hence, &lt;em&gt;is it worth it for maintainers to expend time and energy in curating the software instead of focusing on a collaborator mentality?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For the most part, and most software (especially enterprise) it pays to be a contributor. Being a maintainer is usually low visibility and low payoff. Your PM does not care that you added architecture unit tests or cleaned up the packages in the repo to improve future code changes. Or they may care, but overall the memory of the effort of these changes is low. However, they notice when you can get that next whizz-bang feature out.&lt;/p&gt;
&lt;p&gt;On top of that, as a maintainer, you will most likely have the same amount of work and duties as anyone else, which means you not only have to optimise locally to deliver but also have to constantly use up focus, energy and time to think of how you could evolve the software better. The problem gets exacerbated by the fact more often than not, the kind of changes that help everyone or move the needle on maintaining the software to be more robust is usually medium-big changes that can&#39;t be done in a day or two.&lt;/p&gt;
&lt;p&gt;However, there are distinct advantages to adopting a maintainer mentality. The chief benefit, in my opinion, is that it&#39;s a great way to break through the monotony of the current state of software engineering, which is just yet another flavour of a
CRUD app. Unless you&#39;re in a true deep engineering discipline (think quantum computing, building machine learning models from scratch etc.). Not only that, being in &lt;em&gt;maintainer mode&lt;/em&gt; creates a great intuition for reviewing PRs and system designs, allowing you to spot potential future problems in both without needing to wait for things to play out in production. This will give you the confidence to make technical decisions that don&#39;t sacrifice the health of the software or the service to reach a local maxima since it&#39;s not a zero-sum game.&lt;/p&gt;
&lt;p&gt;To sum up, both choices are good. I think in most instances people can mould their behaviour to either mentality based on the current needs of their productπ and team.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/readme/featured/contributor-relations&quot;&gt;How open source maintainers keep contributors—and themselves—happy&lt;/a&gt; &lt;a href=&quot;https://shiveenp.com/blog/contributor-vs-maintainer-mentality/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.ncbi.nlm.nih.gov/pmc/articles/PMC7959608/&quot;&gt;Software evolution: the lifetime of fine-grained elements&lt;/a&gt; &lt;a href=&quot;https://shiveenp.com/blog/contributor-vs-maintainer-mentality/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
	</entry>
	
	<entry>
		<title>Code Review Principles</title>
		<link href="https://shiveenp.com/blog/code-review-guidelines/"/>
		<updated>2023-04-25T03:55:31Z</updated>
		<id>https://shiveenp.com/blog/code-review-guidelines/</id>
		<content type="html">&lt;p&gt;I have been thinking a lot about code reviews lately and the impact it can have on delivery for teams. Whilst not a topic that has never been touched before, I found the existing material either was too verbose or too much geared towards how to raise a PR rather than how to review them. In light of that, I have compiled a small set of principles to guide them for myself and I am sharing them here.&lt;/p&gt;
&lt;h2 id=&quot;tag-them&quot; tabindex=&quot;-1&quot;&gt;Tag them &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/code-review-guidelines/#tag-them&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In principle, all comments should have a tag. A popular strategy is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nit ⚠️ - optional, feel free to ignore (e.g. [&lt;strong&gt;nit&lt;/strong&gt;]: there is an extra space between these lines)&lt;/li&gt;
&lt;li&gt;followup ♻️ - not optional, let&#39;s ticket it, ship and come back (e.g. [&lt;strong&gt;followup&lt;/strong&gt;]: Let&#39;s clean this file once xyz team finishes project abc)&lt;/li&gt;
&lt;li&gt;blocker 🛑 - fix it right now (e.g. [&lt;strong&gt;blocker&lt;/strong&gt;]: this will make the code here run in O(n^2) instead of O(n) - let&#39;s discuss on zoom)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It&#39;s important to strike the right balance between code correctness and delivery, otherwise it can lead to unfortunate build - comment - fix - build - comment -build again purgatory. Following this approach not only makes the intention of the comments clearer to the PR owner, it makes you as a reviewer think aware of your comment. In several cases, you may find that the thing you were about to write off as a blocker is more of a follow up and that follow up is more of a nit.&lt;/p&gt;
&lt;h2 id=&quot;avoid-being-vague&quot; tabindex=&quot;-1&quot;&gt;Avoid being vague &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/code-review-guidelines/#avoid-being-vague&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In general avoid making vague and &lt;em&gt;maybe&lt;/em&gt; style comments (this is especially true for comments tagged as blocker). If you have an alternative to authors approach mention that, but hand wavy comments help no one. Whilst at it, I have noticed greater engagement when reviewers qualify their comment and think about the pros and cons of their suggestion - it implies that you&#39;re not simply ivory towering your way through the review.&lt;/p&gt;
&lt;h2 id=&quot;confirm-then-suggest&quot; tabindex=&quot;-1&quot;&gt;Confirm, then suggest &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/code-review-guidelines/#confirm-then-suggest&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For most non-trivial suggestions (which should be all of them since we don&#39;t like nits so much) try to validate the proposed solution locally before suggesting. This is not a blanket rule and depends a lot on individual comfort with language, framework, tools etc. But, it&#39;s usually better to conserve the collective mental brainwidth of the team and wrong solution can end up wasting the reviewer and code owners time. Having a seperate copy of the project checked out and use that for code reviews. This approach simplifies separating dev environment from review environment.=&lt;/p&gt;
&lt;h2 id=&quot;nit-as-an-exception&quot; tabindex=&quot;-1&quot;&gt;Nit as an exception &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/code-review-guidelines/#nit-as-an-exception&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Nits end up crowding the review. If it&#39;s a nit, it should appear if it adds value above and beyond adding it to a linter or the company/team code guidelines page would add. Yet, in saying that, there are grey areas. Not all languages lend themselves to a good automated linter and team guidelines can sometimes go stale. In these situations, having a social contract of sorts within the team where people acknowledge that nits are &lt;strong&gt;optional&lt;/strong&gt; . Instead of adding 20 nits on a PR, grab the person who owns the change and walk them through the team code guidelines and add new ones where they don&#39;t exist.&lt;/p&gt;
&lt;h2 id=&quot;prior-art&quot; tabindex=&quot;-1&quot;&gt;Prior Art &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/code-review-guidelines/#prior-art&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.netlify.com/blog/2020/03/05/feedback-ladders-how-we-encode-code-reviews-at-netlify/&quot;&gt;Feedback Ladders: The Code Review System We Follow at Netlify&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.atlassian.com/blog/git/written-unwritten-guide-pull-requests&quot;&gt;The (written) unwritten guide to pull requests - Work Life by Atlassian&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://google.github.io/eng-practices/review/reviewer/&quot;&gt;How to do a code review&lt;/a&gt;&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>On Note Taking Tools</title>
		<link href="https://shiveenp.com/blog/on-note-taking-tools/"/>
		<updated>2022-06-18T15:41:31Z</updated>
		<id>https://shiveenp.com/blog/on-note-taking-tools/</id>
		<content type="html">&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Update 2023&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I have settled back on Bear as I kept coming back to it and the new Bear 2.0 looks amazing 🤩&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I have always been a bit of a bike shedder regarding productivity apps, and note-taking apps are probably the most bike-shedded. Personally and professionally, notes have been integral to my learning journey since I was in University when I started to use them thoughtfully.&lt;/p&gt;
&lt;p&gt;In this post, I will compare various note-taking tools I have used. I will not list the pros and cons per se, but rather my take based on my use cases. The views are anecdotal, and I wish to evaluate them all subjectively.&lt;/p&gt;
&lt;h2 id=&quot;apple-notes&quot; tabindex=&quot;-1&quot;&gt;Apple Notes &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#apple-notes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The humble Apple notes was my first proper foray into a note-taking app that wasn&#39;t a bunch of text files synced through my dropbox.&lt;/p&gt;
&lt;p&gt;I loved using it. Being a native app by Apple, it was snappy and free. I viewed the lack of too many features and stripped-down formatting as a plus. The table feature was a game-changer for me since I tend to organise quite a few things in a matrix-like format. The collaboration and the recently introduced tags were also excellent, although I had already moved on from Apple notes by the time tags were introduced.&lt;/p&gt;
&lt;p&gt;However, as much as I liked using it, I soon ran into limitations with the interface. The search seemed lacking; a simple text search wasn&#39;t cutting it for me. As a software engineer, I also store a lot of code snippets in my notes, which Notes does not support. I also am pretty used to the quick formatting offered by markdown-based tools, which in Notes takes several clicks or keyboard shortcuts to remember. The lack of wiki or backlinking is also a big downside for me as it&#39;s a vital tool to add structure to my notes sprawl.&lt;/p&gt;
&lt;h2 id=&quot;evernote&quot; tabindex=&quot;-1&quot;&gt;Evernote &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#evernote&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I confess that I jumped on the Evernote bandwagon quite late. I tried Evernotes briefly in 2016 and stuck with it for six months. I tried it again in 2021, but this time gave up in less than a day. Both times it failed me on my single most important criterion of note-taking apps: speed.&lt;/p&gt;
&lt;p&gt;Writing in Evernotes felt laggy and devoid of any simpleness to it. I also felt that Evernote was trying to be a kitchen sink for everything productivity-related (tasks, calendars, project management etc.) but with a janky UX. I admit this works for many people, and I know colleagues that breeze through their documents in Evernote like it&#39;s nobody&#39;s business. But, for me, it was a chore.&lt;/p&gt;
&lt;h2 id=&quot;bear&quot; tabindex=&quot;-1&quot;&gt;Bear &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#bear&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I started using Bear about four years ago, which was love at first sight. It felt like a breath of fresh air compared to anything else I had tried. It hit all my standards, and I was sold on the tag-based organisation, so much so that I can&#39;t imagine using anything else to organise my notes anymore.&lt;/p&gt;
&lt;p&gt;Another plus for me was the seamless markdown editing experience. It feels well created and gets out of your way when you need it to but is readily available when needed as well. It&#39;s easy to tell the amount of love and care that has gone into the editing experience for Bear. Bear also has powerful search macros like &lt;code&gt;@todo&lt;/code&gt; to find all notes with open checkboxes and &lt;code&gt;@today&lt;/code&gt; to find all notes created today. I tend to use them quite frequently. Although not as powerful as something like Roam Research or Obsidian, the wiki linking was good enough for my use case, which involves structuring notes that have accumulated a lot of cruft - to tell an unbroken story as much as possible.&lt;/p&gt;
&lt;p&gt;However, it hasn&#39;t always been smooth sailing with Bear. I miss tables that are almost table stakes ;) for most note-taking apps these days. The wiki link, as good as it may be, I wish it had backlinks and, perhaps even better, a graph view similar to Obsidian or LogSeq to understand and consolidate bits of knowledge that get scattered across multiple notes. The app also hasn&#39;t seen any significant changes in the time I have been using it. Bear is good if you want a Markdown-based alternative to Apple Notes, but it was not suited to my workflow for anything more complicated than that.&lt;/p&gt;
&lt;h2 id=&quot;notion&quot; tabindex=&quot;-1&quot;&gt;Notion &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#notion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I tried using it for the first time three years ago, in 2019. I drank the kool-aid before it became hip at the start of the remote work wave. At that time, I felt like I was doing too much, or maybe Notion was not sure.&lt;/p&gt;
&lt;p&gt;Whenever I tried Notion, I kept wasting time trying to work out the compelling features under the hood. I would start with a basic template and then get carried away trying to waste too many hours thinking if what I wanted to put in the tool was a database or not. The constant needing to curate rather than create was a drain. The UX also felt a bit slower than the plain text note-taking, and the no offline access was a pain when I put some camping recipes in it, and I didn&#39;t have access to it when I reached the actual campgrounds.&lt;/p&gt;
&lt;p&gt;However, having given it a go over the last few months, I can conclusively say Notion is on track to be my second brain for the foreseeable future. The thing where I went wrong with Notion was focussing on form over function. These days I write whatever, wherever in Notion and every two weeks, I review my newly created pages and structure them. I heavily use the database function to track my projects and things such as subscriptions, finances and an upcoming Europe trip. This workflow allows me to get stuff in Notion without worrying about &lt;em&gt;where&lt;/em&gt; it needs to go but also provides a framework for patterns to emerge organically.&lt;/p&gt;
&lt;h2 id=&quot;obsidian&quot; tabindex=&quot;-1&quot;&gt;Obsidian &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#obsidian&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I must have been living under a rock since I only learnt about it from a friend who uses it religiously. Notes get stored in plain markdown, which is a plus for longevity, and even though it&#39;s an electron app, I never felt it was slow for day-to-day use. The most robust feature for me was the graph view which automatically finds links between notes based on words and phrases written in other notes. This type of Omnidirectional note linking is advantageous if you are practising the Zettlekasen method of note-taking.&lt;/p&gt;
&lt;p&gt;I stuck with Obsidian for a while but found it unsuitable for me in a few but essential ways. Firstly, the search was lacklustre and confusing even. Compared to searching in various note-taking apps, the search in Obsidian tends not to find what I&#39;m looking for most of the time. Another drawback for me was viewing inline images. I like having images in my notes as I am a visual learner. However, there&#39;s no way to view pictures while editing in Obsidian notes. Last but not least, the mobile app, while well designed to handle the complexity of the problem Obsidian is tackling, seemed cluttered for my day-to-day use and didn&#39;t have the seamless feel I had with other markdown-based apps (like Bear).&lt;/p&gt;
&lt;h2 id=&quot;craft&quot; tabindex=&quot;-1&quot;&gt;Craft &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#craft&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I started using Craft after I got frustrated using Notion. Craft pitches itself as a fast and native alternative to Notion. First impressions were good, it was fast, and the writing felt fluid. It also had tables (hell yeah) and the ability to track Daily notes as an inbuilt feature. I liked that I could also link my calendar and take meeting notes within the same app.&lt;/p&gt;
&lt;p&gt;However, it didn&#39;t work out for me long term. First, Craft may be fast, but it doesn&#39;t match the ease of editing and wrangling text of a simple markdown text editor. The choice of block system (same as the one Notion uses) caused a lot of friction when editing and moving around text which happens a lot when I&#39;m in the first few fluid stages of a new note, especially on mobile. I also found that a big emphasis for the tool was to make text look pretty, which is not a wrong goal, but it seemed to be at odds with how I take notes constantly. I also came spoiled by the tag-based approach to note-taking that started with Bear and not having that shackled my workflow. With all these limitations and a hefty price tag (and probably well-deserved price tag, good native/cloud hybrid apps aren&#39;t cheap) - I decided it was not worth it for me as a long-term tool.&lt;/p&gt;
&lt;h2 id=&quot;drafts&quot; tabindex=&quot;-1&quot;&gt;Drafts &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#drafts&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I learnt about Drafts through one of those Apple app store stories. After trying it for a few weeks and reading this detailed &lt;a href=&quot;https://www.macstories.net/reviews/drafts-5-the-macstories-review/&quot;&gt;review&lt;/a&gt;, I knew this was it. Drafts now serve as my hub for daily notes, random snippets of text, unformed thought, and anything that&#39;s not fully formed yet in my knowledge base. It also has tags that work best for me as an organisation system, and I can quickly jump to different categories of notes. I love the powerful integrations and the uncluttered interface to get some text on the screen as soon as possible.&lt;/p&gt;
&lt;p&gt;However, in saying that, it might not be the app for everyone. Drafts also doesn&#39;t have a polished look that some others like Craft and Notion have. It can be a bit disconcerting to land on an empty note every time.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/on-note-taking-tools/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Over the last few months, I have decided that a single app won&#39;t work for me for my use cases. So, I have decided to adopt a workflow that involves two different apps. All my thoughts, random todos, small and big ideas and initial inspirations of text land in Draft. It has become my most used app, and for a good reason. It&#39;s so simple to defrag my mind and drop everything there. Periodically I look at the notes accumulated in Draft and use the extensions to move data across to Notion or Things (my task management app).&lt;/p&gt;
&lt;p&gt;Notion has become the central hub for my PKM system. All my long-term notes and projects live there, and I actively use them to collaborate with my family.&lt;/p&gt;
&lt;p&gt;The choice of using two apps helps me optimise and use the benefits of both of them together. Drafts with its quickness and rich sharing support for daily things and Notion with its structure and built-in backlinks, as well as database support for long-term storage.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Daily Routine</title>
		<link href="https://shiveenp.com/blog/daily-routine/"/>
		<updated>2022-01-16T15:41:31Z</updated>
		<id>https://shiveenp.com/blog/daily-routine/</id>
		<content type="html">&lt;p&gt;Ever since the pandemic threw the world in chaos, I have been consciously thinking about my work and life and how interconnected they have become, partly due to WFH, partly because I am growing old. Pre pandemic, I never really cared much about daily routines, other than basic things, like I need my coffee by 9 am to function, or, I need to go gym atleast twice a week to not feel shit through the week. However, I have come to appreciate the profound power of routine and structure on my overall mental health and thus in turn on my relationships and the work I produce. Here is my current daily routine,&lt;/p&gt;
&lt;h3 id=&quot;early-morning-7-10-am&quot; tabindex=&quot;-1&quot;&gt;Early Morning (7 - 10 am) &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/daily-routine/#early-morning-7-10-am&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Wake up&lt;/li&gt;
&lt;li&gt;Exercise&lt;/li&gt;
&lt;li&gt;Stretch + Meditate&lt;/li&gt;
&lt;li&gt;Breakfast + Coffee&lt;/li&gt;
&lt;li&gt;Define my intention for the day in the daily log and work on the most important tasks first&lt;/li&gt;
&lt;li&gt;Focussed work&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I avoid looking at any news or internet in the morning and try to conserve my mental energy for the most important tasks of the day. This usually starts by me quickly taking stock of all my main tasks of the day and then setting an intention to complete the top priority task first thing in the morning on my daily notes. I tend to do atleast 1-2 30 minute focus sessions during this time. I follow pomodoro technique for my focus sessions and usually have slack off till 10am.&lt;/p&gt;
&lt;h3 id=&quot;late-morning-10-am-12-pm&quot; tabindex=&quot;-1&quot;&gt;Late Morning (10 am-12 pm) &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/daily-routine/#late-morning-10-am-12-pm&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Focussed work&lt;/li&gt;
&lt;li&gt;Read/respond on Slack&lt;/li&gt;
&lt;li&gt;Walk&lt;/li&gt;
&lt;li&gt;Lunch&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is where I pick up steam on the context gained from morning focus sessions. These may not be the most creative hours for me but I tend to be most output geared during this stretch and it involves either getting clear answers to the tasks I resolved to do in the morning or copius amounts of note taking to make sure I don&#39;t miss things. This is also the time when I respond over slack and plan my lunch for the day. If weather permits, I always tend to go for 30 min walk in this stretch.&lt;/p&gt;
&lt;h3 id=&quot;afternoon-1p-5-pm&quot; tabindex=&quot;-1&quot;&gt;Afternoon (1p - 5 pm) &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/daily-routine/#afternoon-1p-5-pm&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Admin tasks&lt;/li&gt;
&lt;li&gt;Meetings&lt;/li&gt;
&lt;li&gt;Knock out small dev tasks&lt;/li&gt;
&lt;li&gt;Read/response emails&lt;/li&gt;
&lt;li&gt;Read/respond slack&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the time when I can feel the slump slowly creeping in. This is also the block of time when I have the most amount of meetings scheduled. I try to knock out atleast 1 or 2 focus sessions again but it&#39;s a rarity. During this time, if I haven&#39;t made huge breakthrough on my main task in the morning, depending on the urgency, I will shift gears and start focussing on low hanging fruit style things, for example, fix the documentation here, rework the build file there kind of stuff. I also use this time to go over my emails of the day and read or respond to any pending slack messages.&lt;/p&gt;
&lt;h3 id=&quot;evening-5pm-10-pm&quot; tabindex=&quot;-1&quot;&gt;Evening (5pm - 10 pm) &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/daily-routine/#evening-5pm-10-pm&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Dinner&lt;/li&gt;
&lt;li&gt;Spend time with my wife&lt;/li&gt;
&lt;li&gt;Personal Project or Reading&lt;/li&gt;
&lt;li&gt;Reading news (mostly twitter)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the evening, I like to spend time doing non work stuff. This time block ins unstructured and I usually let my mood decided what I feel like doing. Some days it&#39;s cooking, other days I spend my time watching something on Netflix or listening to a Podcast. I try to priortise majority of this block to do atleast one thing with my wife, whether that be playing a board game or watching a TV show. Cooking together has also been a fun activity to do together. As far as news is concerned, I tend to avoid doomscrolling and have no social media apps installed on my phone. If I need to browse twitter I open the web app on my iPad.&lt;/p&gt;
&lt;p&gt;And that&#39;s all. Hopefully this gives you some motivation to structure your day as well!&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Software Is Not A Race</title>
		<link href="https://shiveenp.com/blog/software-is-not-a-race/"/>
		<updated>2021-08-12T08:00:29Z</updated>
		<id>https://shiveenp.com/blog/software-is-not-a-race/</id>
		<content type="html">&lt;p&gt;Too often I see people building software and doing changes in an existing piece of code as if they&#39;re texting one...excruciating...line...at...a...time. As if it&#39;s important to get one disjointed piece of functionality out and making up what&#39;s next on the go.&lt;/p&gt;
&lt;p&gt;This is bad. Software like any other piece of well thought out functionality should be &lt;strong&gt;deliberate&lt;/strong&gt;. Don&#39;t rush into adding a change because the current circumstances confirm it. Check if the change needs to be done based on the requirements of the system. Confirm it with your teammates. More often than not, you will realise that you have missed a key detail of the system. And that the change can be done in another part of the system or not needed at all.&lt;/p&gt;
&lt;p&gt;I have long held the belief that a key and critical milestone in the path of young precocious programmer progressing from making computers go &lt;em&gt;bleep blop&lt;/em&gt; to building programming megastructures, is the ability to pause and question, deeply, pragmatically, WHY. It&#39;s easy to forget in the pace of building a new systems and delivering rapid value, that the best software is the one that&#39;s not needed at all. Each extra line of code in the system brings with itself an extra line to maintain and code reading overhead.&lt;/p&gt;
&lt;p&gt;Since most of the code ever written is write once and maintain/read many times - it is imperative to question the need. Each line of code needs to earn its place there and should have a rock solid reason to be there.&lt;/p&gt;
&lt;p&gt;There are some good strategies that can be applied to enable well thought out software in the org:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;No matter what the change, always communicate the need. Writing a detailed why on pull requests is good enough for trivial changes. For non-trivial changes, write a small RFC or some form of &lt;code&gt;Approach Validation Document&lt;/code&gt; ™ and get a consensus sign off from your team or a subset of the team. This adds a small overhead to delivering your work but in the end your team will thank you for it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Treat your codebase not as a junkyard to throw shit in instead a garden to be cultivated. Encourage this mentality within your team as well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Hone and cultivate a strong bullshit meter for changes that are done to cover and compensate the deficiencies of the system or the process. Quick hacks to get stuff shipped are okay, living with those hacks without even discussing fixing the problems elsewhere seldom is.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;d like to end this post with a caveat that there are cases where it&#39;s necessary to add quick &amp;amp; dirty hacks to fix production or hustle changes to prod to meet a deadline. Such things are a part and parcel of any engineering org to a certain degree and no one should expect you to write a detailed write up for every single commit you make in these cases. But, recognising the time and place for such changes and balancing that with careful followup is the key to not let the system fall under ever increasing tech debt.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Equality Checks in Kotlin With Unstructured Data</title>
		<link href="https://shiveenp.com/blog/equality-checks-in-kotlin-with-unstructured-data/"/>
		<updated>2021-06-17T11:03:44Z</updated>
		<id>https://shiveenp.com/blog/equality-checks-in-kotlin-with-unstructured-data/</id>
		<content type="html">&lt;p&gt;This is a small blog post on equality checks in Kotlin when one of the objects may contain arbitrary data.&lt;/p&gt;
&lt;p&gt;Let&#39;s say you have an object that looks like:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;data class ArbitraryData(val id: UUID, val someData: Map&amp;lt;String, Any&amp;gt;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;where &lt;code&gt;someData&lt;/code&gt; is any json map of data that could contain any untyped data at runtime.&lt;/p&gt;
&lt;p&gt;Now, if you have two instances of this object with the same data and try to compare the two using the regular equals method, you would get a false result.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;arbData1 == arbData2&lt;/code&gt; results in false.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is due to the default Map equality implementation for Kotlin, basing the check on same references for the objects. Hence, even if you have the &lt;code&gt;someData&lt;/code&gt; maps in both objects populated with the same value they will fail.&lt;/p&gt;
&lt;p&gt;To get around this little dilemma, you need to override the default &lt;code&gt;equals&lt;/code&gt; implementation for &lt;code&gt;ArbData&lt;/code&gt; class and manually compare the values.&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ArbitraryData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; UUID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; someData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Map&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Any&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Boolean &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; ArbitraryData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; someData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;entries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;someData&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
	</entry>
	
	<entry>
		<title>Dynamodb Gotchas</title>
		<link href="https://shiveenp.com/blog/dynamodb-gotchas/"/>
		<updated>2021-05-19T09:56:29Z</updated>
		<id>https://shiveenp.com/blog/dynamodb-gotchas/</id>
		<content type="html">&lt;p&gt;This is just a collection of things that required some google searches to resolve while working with DynamoDB at work recently. All the code examples here use Kotlin, though, I&#39;m confident that they would still appear in Java.&lt;/p&gt;
&lt;h2 id=&quot;persisting-a-dynamodb-object-that-extends-a-class&quot; tabindex=&quot;-1&quot;&gt;Persisting a DynamoDb Object that extends a class &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/dynamodb-gotchas/#persisting-a-dynamodb-object-that-extends-a-class&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;DynamoDB doesn&#39;t automatically work with Abstract classes. For example, let&#39;s say you have an &lt;code&gt;abstract class&lt;/code&gt; which contains some common fields (such as the hashKey and the range fields),&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; AbstractBook &lt;span class=&quot;token annotation builtin&quot;&gt;@JvmOverloads&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@DynamoDBHashKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attributeName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;isbn&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; isbn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@DynamoDBTyped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;DynamoDBMapperFieldModel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;DynamoDBAttributeType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;S&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@DynamoDBRangeKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;attributeName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; bookType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then you define the actual class, that extends this base class,&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Novella &lt;span class=&quot;token annotation builtin&quot;&gt;@JvmOverloads&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		isbn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		bookType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; content&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AbstractBook&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isbn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you try to use a &lt;code&gt;DynamoDBMapper&lt;/code&gt; to save the object it will throw an exception.&lt;/p&gt;
&lt;p&gt;To fix the exception, you&#39;ll need to add the &lt;code&gt;@DynamoDBDocument&lt;/code&gt; to the AbstractClass to let the mapper know that it&#39;s the abstract version of the actual persisted entity. This is what the JavaDoc for the annotation says:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An annotation that marks a class which can be serialised to a DynamoDB document or sub-document. Behaves exactly the same as DynamoDBTable, but without requiring you to specify a tableName.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which means that this annotation is necessary to serialise objects types that are not directly part of the actual stored object type.&lt;/p&gt;
&lt;h2 id=&quot;persisting-a-list-of-objects-in-dynamodb&quot; tabindex=&quot;-1&quot;&gt;Persisting a List of Objects in DynamoDB &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/dynamodb-gotchas/#persisting-a-list-of-objects-in-dynamodb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another little snowflake behaviour I encountered was when persisting a list of objects. Let&#39;s say we go ahead and add some more data to the Novella object, like a List of Publishers.&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Publishers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; address&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Novella &lt;span class=&quot;token annotation builtin&quot;&gt;@JvmOverloads&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		isbn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		bookType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; content&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 
        &lt;span class=&quot;token annotation builtin&quot;&gt;@DynamoDBTypeConvertedJson&lt;/span&gt; 
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; publishers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Publishers&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AbstractBook&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isbn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;@DynamoDBTypeConvertedJson&lt;/code&gt; is the annotation DynamoDB recommends for storing objects when using DynamoDBMapper. It has a strange behaviour where it can auto serialise a &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; but it loses type information on deserialisation and deserialises the object as an amorphous map. Which means you get exceptions like:
&lt;code&gt;java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to Publisher&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Based on this &lt;a href=&quot;https://stackoverflow.com/questions/30793481/dynamodb-jsonmarshaller-cannot-deserialize-list-of-object&quot;&gt;thread&lt;/a&gt; this has to do with type erasure, wherein &lt;code&gt;T : Object&lt;/code&gt; which results in bad behaviour at deserialisation time (the default Jackson marshaller is smart enough during serialisation).&lt;/p&gt;
&lt;p&gt;The best way to solve this is to define a custom serialiser and deserialiser for your object.&lt;/p&gt;
&lt;p&gt;So, in this case it&#39;s a matter of defining a class like,&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; PublishersMapListConverter &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DynamoDBTypeConverter&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Publishers&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; objectMapper &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jacksonObjectMapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;convert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publishers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Publishers&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; objectMapper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writeValueAsString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publishers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;unconvert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publishers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Publishers&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TypeReference&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Publishers&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; objectMapper&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;publishers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; type&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then adding the &lt;code&gt;@DynamoDBTypeConverted(converter = PublishersMapListConverter::class)&lt;/code&gt;,&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Novella &lt;span class=&quot;token annotation builtin&quot;&gt;@JvmOverloads&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        isbn&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        bookType&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; content&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token annotation builtin&quot;&gt;@DynamoDBTypeConverted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;converter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; PublishersMapListConverter&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; publishers&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Publishers&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AbstractBook&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isbn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bookType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hopefully this post helps someone else save some time as well.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Building a Reactive Oauth Client App with SpringBoot and Kotlin Coroutines</title>
		<link href="https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/"/>
		<updated>2020-06-09T10:40:49Z</updated>
		<id>https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/</id>
		<content type="html">&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt;Background &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/#background&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this post, I’ll walk through two new exciting things that have happened in the spring ecosystem recently. First is spring webflux support for kotlin &lt;a href=&quot;https://kotlinlang.org/docs/reference/coroutines-overview.html&quot;&gt;couroutines&lt;/a&gt; and second is the overhaul of spring security, and the addition of the out of the box oauth2 client support for &lt;a href=&quot;https://spring.io/blog/2018/07/03/spring-social-end-of-life-announcement&quot;&gt;social logins&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For me one of the most impactful new features is the integration with spring coroutines throw the &lt;a href=&quot;https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/index.html&quot;&gt;Flow&lt;/a&gt; primitive. The integration makes writing reactive code a lot more straightforward - no more &lt;code&gt;subscribeOn&lt;/code&gt; and &lt;code&gt;observeOn&lt;/code&gt; operations. Instead of thinking in terms of &lt;code&gt;Mono&lt;/code&gt; and &lt;code&gt;Flux&lt;/code&gt; based primitives borrowed from &lt;a href=&quot;https://github.com/reactor/reactor-core&quot;&gt;reactor&lt;/a&gt;, it allows a straightforward way to generate cold streams instead. Kotlin flow allows us to write purely asynchronous code in an sequential/imperative manner - which in turn means an existing codebase using blocking code can be converted to use the non-reactive paradigm a lot easier. Another advantage of using coroutines based non-blocking code is that coroutines have been implemented as &lt;a href=&quot;https://medium.com/@elizarov/blocking-threads-suspending-coroutines-d33e11bf4761&quot;&gt;lightweight threads&lt;/a&gt; whereas schedulers with reactor can incur a lot of performance overhead as they have to context switch between threads. As we progress forward in this post, people who are already familiar with composing non-reactive code in webflux will see what I mean.&lt;/p&gt;
&lt;h2 id=&quot;lets-get-started&quot; tabindex=&quot;-1&quot;&gt;Let&#39;s get started! &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/#lets-get-started&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To demonstrate the way this works, we will try to develop an asynchronous api that connects to a GitHub account and gets all the starred repos for a user. The api will use spring webflux with Kotlin Flow and we will integrate that with the spring oauth2 client to ensure the user is logged in.&lt;/p&gt;
&lt;p&gt;To start off, let’s create a new gradle project in whatever IDE you prefer (I use Intellij IDEA) and add the following set of dependencies:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.boot:spring-boot-starter-webflux&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.boot:spring-boot-starter-security&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.security:spring-security-oauth2-client&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.springframework.security:spring-security-oauth2-jose&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;com.fasterxml.jackson.module:jackson-module-kotlin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;io.projectreactor.kotlin:reactor-kotlin-extensions&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.jetbrains.kotlin:kotlin-reflect&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.jetbrains.kotlinx:kotlinx-coroutines-core&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
implementation&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.jetbrains.kotlinx:kotlinx-coroutines-reactor&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once setup, lets start building our controller first, creating a new file &lt;code&gt;Router.kt&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Router &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; coRouter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;getStarredRepos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see we have simply used the normal way of defining a new spring based REST api controller but instead of using the standard router annotations we took the approach of using the coRouter. It’s more of a personal preference since it allows me to see my whole api surface area in a more concise format. However, you could easily replace it with the more common Spring MVC style annotation based approach.&lt;/p&gt;
&lt;p&gt;Next we will build a handler that let’s us handle our service response.&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Service&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getStarredRepos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerResponse &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bodyAndAwait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserStarredRepos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nothing special about the handler except two main distinctions when compared to equivalent reactor based approach. First is that we have made the function &lt;code&gt;getStarredRepos()&lt;/code&gt; a suspending function - which means it tells the compiler that this code will be run inside a couroutines context. The second interesting thing to note is theat we used a &lt;code&gt;bodyAndAwait()&lt;/code&gt; instead of &lt;code&gt;body()&lt;/code&gt; method for a server response. This extension allows us to correctly get the body from suspending couroutine context while the service is generating a response without blocking the calling thread.&lt;/p&gt;
&lt;p&gt;Now let’s jump into the service code - we will use the new spring Oauth2 client with comes with first class webflux support to make our app support the GitHub Oauth login.&lt;/p&gt;
&lt;p&gt;To begin with, &lt;a href=&quot;https://github.com/spring-projects/spring-security/tree/5.3.3.RELEASE/samples/boot/oauth2login#github-register-application&quot;&gt;register a new app in Github&lt;/a&gt;. Once done, add a new &lt;code&gt;application.yml&lt;/code&gt; file and add your oauth details like:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;spring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;security&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;oauth2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token key atrule&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;token key atrule&quot;&gt;registration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;token key atrule&quot;&gt;github&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;clientId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &amp;lt;add&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;here&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token key atrule&quot;&gt;clientSecret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &amp;lt;add&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;github&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;secret&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;here&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells the Oauth client what credentials to use when redirecting a non authenticated user to Github authentication page. The default authorization callback url setup by the spring is: &lt;code&gt;http://localhost:8080/login/oauth2/code/github&lt;/code&gt; .In and of itself all oauth client will do is grab the github authorization grant and store that data in a &lt;code&gt;JSESSION&lt;/code&gt; cookie. If that&#39;s all you want to do then that&#39;s fine - however, I would also like to show the user their starred github repositories. To achieve that we need to be able to somehow get the access token and make an authenticated call on behalf of the user. Spring provides a nice and secure interface to achieve that as well - enter the authenticated webclient.&lt;/p&gt;
&lt;p&gt;Webclient is the reactive counterpart of the old and trusty RestTemplate from the Spring MVC days introduced in Spring webflux. It allows us to make calls to APIs in a non-blocking api and comes with nice composition and testing support. In our case, we will build a spring config that will populate the current context with an authenticated webclient for the current logged in user.&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; GithubWebClientConfig &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;webClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clientRegistrations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ReactiveClientRegistrationRepository&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  authorizedClients&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerOAuth2AuthorizedClientRepository&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WebClient&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; oauth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ServerOAuth2AuthorizedClientExchangeFilterFunction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;clientRegistrations&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; authorizedClients&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        oauth&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setDefaultOAuth2AuthorizedClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; WebClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;oauth&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key to setting the right authentication credentials is the line &lt;code&gt;oauth.setDefaultOAuth2AuthorizedClient(true)&lt;/code&gt; which allows the github client bean to be automatically authenticated with the current users github tokens, simple and delightful.&lt;/p&gt;
&lt;p&gt;Now we&#39;ll go ahead and wire up the last piece of this puzzle which is the service layer that allows us to tie the authenticated webclient with an API call. So let&#39;s go ahead and do that:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WebClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserStarredRepos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Flow&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;String&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; client
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.github.com/user/starred?page=1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;retrieve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bodyToFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To explain a bit of what&#39;s going in this code snippet, we have created another suspendable function &lt;code&gt;getUserStaarredRepos()&lt;/code&gt; that retrieves the starred repos from github and uses our pre authenticated oauth webclient to do so. One callout here would be the return type which is of the type &lt;code&gt;Flow&amp;lt;String&amp;gt;&lt;/code&gt; . This possible due to Kotlins extension based programming approach where the coroutines team have created an extension for the webclient which is functionally equal to reactor&#39;s &lt;code&gt;bodyToFlux()&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;That concludes our build for this app. To test, let&#39;s fire it up using the ever helpful gradle &lt;code&gt;bootRun&lt;/code&gt; command (you should run this in your terminal with the current directory set to this sample project root):&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;./gradlew bootRun&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once done, this will start up our Springboot app and you should be able to navigate to &lt;a href=&quot;http://localhost:8080&quot;&gt;localhost:8080&lt;/a&gt; to see it in action.&lt;/p&gt;
&lt;h3 id=&quot;comparing-with-non-reactive-flow&quot; tabindex=&quot;-1&quot;&gt;Comparing with non-reactive flow &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/#comparing-with-non-reactive-flow&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;https://github.com/clojj&quot;&gt;clojj&lt;/a&gt; the example project for this post has been updated. It now supports demonstrating how the HTTP responses work for Flow type vs sending a normal response. To demonstrate, we now have two endpoints that return the same data as the endpoint described originally in this post but have been segregated into a non-blocking vs blocking function. Our &lt;code&gt;Router.kt&lt;/code&gt; file now looks like:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@RestController&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; Router &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@Bean&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;handler&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Handler&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; coRouter &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;flow&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;getStarredReposAsFlow&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;GET&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;list&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handler&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;getStarredReposAsList&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where we now have a &lt;code&gt;getStarredReposAsList&lt;/code&gt; endpoint to demonstrate sending the data as List. Consequently, the handler and service now look like,&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Handler.kt&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; service&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Service&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getStarredReposAsFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerResponse &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; starredRepos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserStarredReposAsFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bodyAndAwait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;starredRepos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getStarredReposAsList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerRequest&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ServerResponse &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; starredRepos &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; service&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUserStarredReposAsList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bodyValueAndAwait&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;starredRepos&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Service.kt&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Service&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Service&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WebClient&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserStarredReposAsFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Flow&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Repository&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; client
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.github.com/user/starred?page=1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;retrieve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bodyToFlow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;suspend&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getUserStarredReposAsList&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Repository&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; client
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.github.com/user/starred?page=1&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;retrieve&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;awaitBody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As expected, the Flow based non-blocking endpoint does not know what the full size of the response will be so it adds the &lt;code&gt;transfer-encoding: chunked&lt;/code&gt; header. On the other hand, the blocking type endpoint which returns a list directly knows the response size beforehand, so it adds the &lt;code&gt;Content-Length&lt;/code&gt; header letting the client know the size of the payload being received straightaway.&lt;/p&gt;
&lt;p&gt;Depending on the specs of your application and how you want the browser to behave it is probably worthwhile understanding which of these two http responses suit your usecase.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/#conclusion&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As someone who moved from springboot with spring mvc building blocking code to using spring webflux and spending hours to understand the non-blocking paradigm - the Flow support in webflux is a welcome evolution. The potential of migrating existing codebases to non-blocking stream based paradigm has been greatly amplified, and I can already see the impact it is having in making my own side projects a lot easier to build and reason about!&lt;/p&gt;
&lt;p&gt;If you&#39;d like to experiment yourself the source code for this post can be found on &lt;a href=&quot;https://github.com/shavz/spring-reactive-kotlin-oauth2-github-example&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;references&quot; tabindex=&quot;-1&quot;&gt;References: &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/spring-boot-reactive-oauth-client-with-coroutines/#references&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;https://medium.com/better-programming/asynchronous-data-loading-with-new-kotlin-flow-233f85ae1d8b&lt;/li&gt;
&lt;li&gt;https://spring.io/blog/2019/04/12/going-reactive-with-spring-coroutines-and-kotlin-flow&lt;/li&gt;
&lt;li&gt;https://medium.com/@elizarov/execution-context-of-kotlin-flows-b8c151c9309b&lt;/li&gt;
&lt;li&gt;https://todd.ginsberg.com/post/springboot-reactive-kotlin-coroutines/&lt;/li&gt;
&lt;li&gt;https://spring.io/guides/tutorials/spring-boot-oauth2/&lt;/li&gt;
&lt;li&gt;https://spring.io/guides/tutorials/spring-boot-oauth2/#github-register-application&lt;/li&gt;
&lt;li&gt;https://www.httpwatch.com/httpgallery/chunked/&lt;/li&gt;
&lt;/ol&gt;
</content>
	</entry>
	
	<entry>
		<title>Right Way To Shadow Jar When Using Jetty With Http4k</title>
		<link href="https://shiveenp.com/blog/fix-shadow-jar-http4k-jetty/"/>
		<updated>2020-04-18T03:10:59Z</updated>
		<id>https://shiveenp.com/blog/fix-shadow-jar-http4k-jetty/</id>
		<content type="html">&lt;p&gt;If you’ve been spring boot for a while, you’re probably familiar with Spring’s bootJar functionality that lets you create a new executable “fat jar” with all its dependencies pre defined. However, while I was building a kotlin app using http4k, which doesn’t come with any such built in tooling I had to resort to using the shadowJar plugin to build a fat jar.&lt;/p&gt;
&lt;p&gt;However post deploy, the app stopped starting up and I noticed the following error in the logs:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Exception &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; thread “main” java.lang.ExceptionInInitializerError
	at org.eclipse.jetty.http.MimeTypes&lt;span class=&quot;token variable&quot;&gt;$Type&lt;/span&gt;.&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;init&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MimeTypes.java:98&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.http.MimeTypes&lt;span class=&quot;token variable&quot;&gt;$Type&lt;/span&gt;.&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;clinit&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MimeTypes.java:56&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.http.MimeTypes.&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;clinit&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MimeTypes.java:175&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.server.handler.ContextHandler.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ContextHandler.java:806&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.servlet.ServletContextHandler.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ServletContextHandler.java:275&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AbstractLifeCycle.java:72&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ContainerLifeCycle.java:169&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ContainerLifeCycle.java:110&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AbstractHandler.java:100&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.websocket.server.WebSocketHandler.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;WebSocketHandler.java:84&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AbstractLifeCycle.java:72&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ContainerLifeCycle.java:169&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.server.Server.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Server.java:407&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ContainerLifeCycle.java:110&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.server.handler.AbstractHandler.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AbstractHandler.java:100&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.server.Server.doStart&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Server.java:371&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AbstractLifeCycle.java:72&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at org.http4k.server.Jetty&lt;span class=&quot;token variable&quot;&gt;$toServer&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$3&lt;/span&gt;.start&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jetty.kt:33&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at io.taggit.AppKt.main&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App.kt:175&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	at io.taggit.AppKt.main&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;App.kt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
Caused by: java.lang.ArrayIndexOutOfBoundsException: &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
	at org.eclipse.jetty.http.PreEncodedHttpField.&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;clinit&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;PreEncodedHttpField.java:68&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
	… &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Perplexed, since when I used the same plugin with another one of my projects I had never noticed this error.&lt;/p&gt;
&lt;p&gt;After much googling, turns out Jetty bundles HttpField encoders by referencing &lt;code&gt;META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder&lt;/code&gt; so when shadow jar created the fat jar it didn’t have the data that Jetty was looking for which in turn meant Jetty threw an initialisation exception.&lt;/p&gt;
&lt;p&gt;The fix?&lt;/p&gt;
&lt;p&gt;Adding the &lt;code&gt;mergeServiceFiles()&lt;/code&gt; attribute to the shadowJar task, as per the shadow documentation &lt;a href=&quot;https://imperceptiblethoughts.com/shadow/configuration/merging/#merging-service-descriptor-files&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-groovy&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;shadowJar &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    baseName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ‘taggit&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;api’
    zip64 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;mergeServiceFiles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And voila, the service was back up and running again. This is becauase multiple libraries potentially use the same service descriptor files (usually &lt;code&gt;META-INF&lt;/code&gt;) and in case of creating fat jars its generally desired to merge the service descriptors to make sure all libraries have their service descriptors loaded at runtime.&lt;/p&gt;
&lt;p&gt;P.S. - It might be worth mentioning here as well that the reason I didn’t encounter the same error for one of my other services is due to that project using Netty as the underlying webserver.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Brows3r - Pure Kotlin S3 Browser</title>
		<link href="https://shiveenp.com/blog/introducing-bows3r-small-s3-web-browser-written-in-pure-kotlin/"/>
		<updated>2019-07-29T08:52:50Z</updated>
		<id>https://shiveenp.com/blog/introducing-bows3r-small-s3-web-browser-written-in-pure-kotlin/</id>
		<content type="html">&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt;Background &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/introducing-bows3r-small-s3-web-browser-written-in-pure-kotlin/#background&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Recently I found myself with some extra development time on hand. Now usually, most of my projects start off as big grand ideas and as soon as I start working on them, I lose steam or life comes in the way and things just sit their eating dust as a private github repo.&lt;/p&gt;
&lt;p&gt;However, I stumbled upon &lt;a href=&quot;https://github.com/kwebio/kweb-core&quot;&gt;Kweb&lt;/a&gt; which is a server side rendered web app building library, but written entirely in Kotlin. Kweb provides a nice &lt;a href=&quot;https://en.wikipedia.org/wiki/Domain-specific_language&quot;&gt;dsl&lt;/a&gt; like interface to build web apps by programmatically defining the html elements for the app instead of writing the html and CSS by hand. For those who don&#39;t know, Kotlin provides a really nice way to build &lt;a href=&quot;https://kotlinlang.org/docs/reference/type-safe-builders.html&quot;&gt;type safe declarative builders&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Since it&#39;s all created declarative in the kotlin code, we get access coroutines, extensions and all the nice things that make working with Kotlin such a joy. For those of you who have worked with pure code based server side rendered frameworks before, this might remind you of &lt;a href=&quot;https://vaadin.com/&quot;&gt;Vaadin&lt;/a&gt; which is an industry leader in its space, but there are subtle and not so subtle differences which you can be found on the &lt;a href=&quot;http://docs.kweb.io/en/latest/faq.html&quot;&gt;Kweb FAQS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Going through their codebase, I though it would be a really good opportunity to try and a build a quick and easy app by just using pure kotlin. Why you ask? Cause it was quick and I could smash it out in a few hours.&lt;/p&gt;
&lt;h2 id=&quot;simple-kotlin-s3-client&quot; tabindex=&quot;-1&quot;&gt;Simple Kotlin S3 Client &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/introducing-bows3r-small-s3-web-browser-written-in-pure-kotlin/#simple-kotlin-s3-client&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To start off, I wrote a basic S3 browsing class, starting off small - I copied the code from AWS examples on creating a new S3 client and then added the ability to search for all public keys in a given bucket and get some metadata and download links:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;S3Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; endpoint&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; bucketName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;


    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AmazonS3ClientBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;standard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withPathStyleAccessEnabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEndpointConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AwsClientBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;EndpointConfiguration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpoint&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ap-southeast-2&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;listAllKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;S3Data&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; req &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ListObjectsV2Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withBucketName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bucketName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withMaxKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; keyList &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mutableListOf&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;S3Data&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listObjectsV2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;objectSummaries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            keyList&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;S3Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;endpoint&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;bucketName&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toDouble&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000.0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastModified&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; keyList
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the &lt;code&gt;listAllKeys()&lt;/code&gt; functions returns a list of all keys in that given bucket, which I can then map to a custom S3Data class:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;S3Data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; key&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; downloadUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; size&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Double&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; lastModifiedAt&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;building-the-ui-with-kweb&quot; tabindex=&quot;-1&quot;&gt;Building the UI with Kweb &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/introducing-bows3r-small-s3-web-browser-written-in-pure-kotlin/#building-the-ui-with-kweb&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once done, I got cracking on the UI interface. I wanted something quick and simple, crude even, just to demonstrate that it all works as proposed. So I setup a container with some form fields, a search button and a table to input the S3 region link and the name of the bucket.&lt;/p&gt;
&lt;p&gt;I also needed a table to display all the keys (sans pagination, who builds pagination in PoCs anyway? 🤠). To enable holding the data, I used something called &lt;a href=&quot;https://github.com/kwebio/kweb-core/blob/master/src/main/kotlin/io/kweb/state/KVar.kt&quot;&gt;KVAR&lt;/a&gt; which is simply a state store used by Kweb to support propagating state changes to the web app via &lt;a href=&quot;https://en.wikipedia.org/wiki/Observer_pattern&quot;&gt;Observer Pattern&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The following code fragment gets the initialises the S3 data Kvar (setup as an empty list initially) - which eventually propagates it to the table:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fomantic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;main&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fomantic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;segment&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fomantic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;header&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Welcome to S3 Browser 💻&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; keyData &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;KVar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;emptyList&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;S3Data&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; loader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui active text loader&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addText&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Retrieving keys...&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    loader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui disabled text loader&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;createInputSegment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;loader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; keyData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;createKeysTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, here is where Kwebs deep integration with kotlin really comes in handy, since it allows us to use &lt;a href=&quot;https://kotlinlang.org/docs/reference/coroutines-overview.html&quot;&gt;kotlin coroutines&lt;/a&gt; to handle tasks with considerable i/o (such as retrieving data from an AWS bucket). I have recently started using coroutines frequently in production code and I can without doubt say they&#39;re the best way to write asynchronous tasks without worrying about threads. The low touch syntax setup and the results are so easy it almost feels like cheating.&lt;/p&gt;
&lt;p&gt;The following code fragment uses the &lt;code&gt;S3Client&lt;/code&gt; introduced earlier to launch a coroutine and when the user hits the search button, and displays a loading icon until all the data is retrieved or an error is thrown:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; ElementCreator&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DivElement&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createInputSegment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    loader&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Element&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    keyData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; KVar&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;S3Data&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fomantic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vertical&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;segment&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fomantic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ui&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;input&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; endpointInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; InputType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; placeholder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Enter S3 Endpoint Url&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; bucketInput &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; InputType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; placeholder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Enter S3 Bucket Name&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui primary button&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Search&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;on&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;click&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                GlobalScope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;launch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    loader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui active text loader&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; s3Client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;S3Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpointInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; bucketInput&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        keyData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; s3Client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listAllKeys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ex&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ERROR_TOAST&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        loader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui disabled text loader&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;keyData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isNotEmpty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;execute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SUCCESS_TOAST&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        loader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui disabled text loader&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So far so good, now that we have successfully pulled in the data in out Kvar container, we can start rendering a table. Now I also wanted to show a nice little icon to show that the retrieved object was a file - and also allow the ability to click name of the key as a link so the user can download.&lt;/p&gt;
&lt;p&gt;Now Kweb as far as I could tell didn&#39;t have the ability to specify that via a DSL object, however, it does provide the ability to specify nested HTML inside a table element to add my own custom behaviour.&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; ElementCreator&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;DivElement&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createKeysTable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    keyData&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; KVar&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;List&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;S3Data&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;class&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ui celled striped table&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;thead&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Key&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;File Size (in KB)&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;th&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Last Modified At&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;tbody&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            keyData&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token function&quot;&gt;tr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-lable&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Key&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;innerHTML&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;i class=&#92;&quot;file outline icon&#92;&quot;&gt;&amp;lt;/i&gt; &amp;lt;a target=&#92;&quot;_blank&#92;&quot; href=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;downloadUrl&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; download=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;key&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;/a&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-lable&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;File Size&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;size&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; KB&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                        &lt;span class=&quot;token function&quot;&gt;td&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mapOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;data-lable&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Last Modified At&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;it&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lastModifiedAt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above creates a new table and generates a new row in the table for each public key present in the provided bucket. If no data is present, nothing gets rendered.&lt;/p&gt;
&lt;p&gt;This probably also the time to give a shoutout to the Kweb creators for an integration with &lt;a href=&quot;https://fomantic-ui.com/&quot;&gt;Fomantic UI&lt;/a&gt; which comes pre-configured with nice UI elements. Although, the integration doesn&#39;t end there and there is a nice APi for anyone to write a new plugin with their favourite UI elements library.&lt;/p&gt;
&lt;h2 id=&quot;final-notes&quot; tabindex=&quot;-1&quot;&gt;Final Notes &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/introducing-bows3r-small-s3-web-browser-written-in-pure-kotlin/#final-notes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;And that is all that&#39;s needed to write a simple S3 browsing web app using Kweb. &lt;a href=&quot;https://secure-scrubland-34237.herokuapp.com/&quot;&gt;Here&lt;/a&gt; is the app deployed on heroku and the full working code is on &lt;a href=&quot;https://github.com/shavz/Bows3r&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Gif Demo:&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://imgur.com/YoJdUxj.gif&quot; alt=&quot;&quot;&gt;&lt;/figure&gt;
&lt;p&gt;Youtube Demo:&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/0soMtA2vUSo&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
</content>
	</entry>
	
	<entry>
		<title>Test Multipart/Form Data with WebTestClient</title>
		<link href="https://shiveenp.com/blog/sending-multipart-form-data-using-spring-webtestclient/"/>
		<updated>2019-04-21T05:52:55Z</updated>
		<id>https://shiveenp.com/blog/sending-multipart-form-data-using-spring-webtestclient/</id>
		<content type="html">&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt;Background &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/sending-multipart-form-data-using-spring-webtestclient/#background&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For the past year or so, I have been working extensively with spring, especially spring webflux; building scalable reactive micro services for our customers.&lt;/p&gt;
&lt;p&gt;Coming from spring MVC, learning webflux and getting used to reactive programming in general has been a great and worthy learning experience and I highly suggest going through the &lt;a href=&quot;https://shiveenp.com/blog/sending-multipart-form-data-using-spring-webtestclient/#References&quot;&gt;references&lt;/a&gt; section if you haven’t heard of reactive programming and/or have been thinking about giving it a go and don’t know where to start. But essentially reactive programming involves a model of creating, requesting and manipulating data in a controllable (from a consumers perspective) and non-blocking manner.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#webtestclient&quot;&gt;WebTestClient&lt;/a&gt; is a reactive testing high level http client with fluent assertions, packaged in spring web flux. Recently, while integration testing an application that accepted data as &lt;a href=&quot;https://tools.ietf.org/html/rfc7578&quot;&gt;multipart/form-data&lt;/a&gt; I had to figure out how to test the data effectively using the webtestclient and personally found the lack of comprehensive resources on the internet lacking, so I wrote this blogpost to share my own learnings.&lt;/p&gt;
&lt;h2 id=&quot;web-form-testing-with-webflux&quot; tabindex=&quot;-1&quot;&gt;Web Form Testing with Webflux &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/sending-multipart-form-data-using-spring-webtestclient/#web-form-testing-with-webflux&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s suppose that we’re trying to send the request to fill a form api that accepts a document (image, text, plain binary etc.) and some textual data.&lt;/p&gt;
&lt;p&gt;To aid with our example, lets imagine the form is a profile setup for an document share service and takes the following input:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Profile Image (&lt;em&gt;api label: profileImage&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Username (&lt;em&gt;api label: username&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Email (&lt;em&gt;api label: email&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;PDF document to share (&lt;em&gt;api label: userDocument&lt;/em&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For us to begin sending the data, we’ll use the spring library called &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/MultipartBodyBuilder.html&quot;&gt;MultipartBodyBuilder&lt;/a&gt; which provides a nice api for setting up the body for multipart requests.&lt;/p&gt;
&lt;p&gt;To send the first part, the profile image we can set it up as:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; bodyBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MultipartBodyBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;profileImage&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ClassPathResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test-image.jpg&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=profileImage; filename=profile-image.jpg&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To explain a bit about what’s going on there, we’re simply telling the body builder to upload an image found in &lt;code&gt;src/test/resources&lt;/code&gt; folder with the name &lt;code&gt;test-image.jpg&lt;/code&gt; as the profile image part of this body. The real kicker here is setting up the &lt;strong&gt;Header&lt;/strong&gt; part as that is what’s used by the webtestclient internals (specifically the &lt;a href=&quot;https://github.com/synchronoss/nio-multipart&quot;&gt;Synchronoss-nio&lt;/a&gt; library which webflux uses internally) to determine the type of form data being sent and how to process it.&lt;/p&gt;
&lt;p&gt;Also, note that the real file name that will get uploaded in the web server receiving the request is the &lt;code&gt;profile-image.jpg&lt;/code&gt; filename that gets sent as part of the headers,.&lt;/p&gt;
&lt;p&gt;Similar to the profile image, we can also send the document part of the whole request payload:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userDocument&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ClassPathResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;user-document.pdf&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=userDocument; filename=my-thesis.pdf&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Similar to the previous payload we test the body builder  💪 to read a file in the test resources folder called &lt;code&gt;user-document.pdf&lt;/code&gt;  as bytes and send the document with the name &lt;code&gt;my-thesis.pdf&lt;/code&gt; to the form web api.&lt;/p&gt;
&lt;p&gt;As you can already see, compared to some other ways of doing it, such as in this &lt;a href=&quot;https://www.baeldung.com/spring-rest-template-multipart-upload&quot;&gt;excellent blog&lt;/a&gt; , using the MultipartBodyBuilder is rather conveneient.&lt;/p&gt;
&lt;p&gt;Now for the last two remaining pieces of the form api, which are usually only plain text, we can set them up as:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shiveenpandita&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MediaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEXT_PLAIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=username&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-type&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shiveenpandita@gmail.com&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MediaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEXT_PLAIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=email&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-type&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Woohoo! 🎉 We’ve got all our form fields wired now.&lt;/p&gt;
&lt;p&gt;Now to see it all in action and bring it all together, we can simply setup a spring integration test and use our freshly setup body builder as:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@RunWith&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SpringRunner&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;java&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@SpringBootTest&lt;/span&gt;
&lt;span class=&quot;token annotation builtin&quot;&gt;@AutoConfigureWebTestClient&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; WebClientTest &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; webclient&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; WebTestClient

    &lt;span class=&quot;token annotation builtin&quot;&gt;@Test&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;`test webform api`&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; bodyBuilder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MultipartBodyBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;profileImage&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ClassPathResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test-image.jpg&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=profileImage; filename=profile-image.jpg&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;userDocument&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ClassPathResource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;test-document.pdf&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readBytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=userDocument; filename=my-thesis.pdf&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;username&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shiveenpandita&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MediaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEXT_PLAIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=username&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-type&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;part&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;shiveenpandita@gmail.com&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; MediaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TEXT_PLAIN&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-Disposition&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;form-data; name=email&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Content-type&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;text/plain&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

        webClient&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/v1/test-api&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;contentType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;MediaType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MULTIPART_FORM_DATA&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;BodyInserters&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fromMultipartData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;bodyBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exchange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expectStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOk
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code snippet will successfully send the required data to our test api and the webtestclient asserts that the response is 200 OK.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://media.giphy.com/media/l0ErKDci4GgPkcAF2/giphy.gif&quot; alt=&quot;&quot;&gt;&lt;/figure&gt;
&lt;h2 id=&quot;references&quot; tabindex=&quot;-1&quot;&gt;References &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/sending-multipart-form-data-using-spring-webtestclient/#references&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.reactivemanifesto.org/&quot;&gt;Reactive Manifesto&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#spring-webflux&quot;&gt;Spring Webflux&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/lucamezzalira/awesome-reactive-programming&quot;&gt;Awesome-List&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content>
	</entry>
	
	<entry>
		<title>Simple Oembed Service Using Http4k</title>
		<link href="https://shiveenp.com/blog/simple-oembed-service-using-http4k/"/>
		<updated>2019-03-18T00:27:06Z</updated>
		<id>https://shiveenp.com/blog/simple-oembed-service-using-http4k/</id>
		<content type="html">&lt;p&gt;Recently on my usual #githunt prowl I came across a new promising http library called &lt;a href=&quot;https://github.com/http4k/http4k/&quot;&gt;http4k&lt;/a&gt;. The library is based on the philosophy of &lt;strong&gt;Application as Function&lt;/strong&gt; based on the twitter paper &lt;a href=&quot;https://monkey.org/~marius/funsrv.pdf&quot;&gt;Your Server as a Function&lt;/a&gt; and promises a lightweight a server toolkit alongwith a very modular approach to adding functionality on top of the core set of capabilities built in. But the best part of all, http4k is written in pure Kotlin and follows a consistent functional approach in handling http services.&lt;/p&gt;
&lt;p&gt;Now, as much as a like the kitchen sink approach of Spring framework in getting an almost enterprise ready service running with a couple of tutorials and minimal effort , I really wanted to try my hands at something lighter and different.&lt;/p&gt;
&lt;h2 id=&quot;setting-up-a-micro-server-with-http4k&quot; tabindex=&quot;-1&quot;&gt;Setting up a micro server with http4k &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/simple-oembed-service-using-http4k/#setting-up-a-micro-server-with-http4k&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Http4k provides a simple yet highly configurable way to setup a microserver using nothing but just the core client and an underlying webserver of choosing.&lt;/p&gt;
&lt;p&gt;To start off, create a new base gradle project in Intellij or any IDE of your choosing and add the following dependencies:&lt;/p&gt;
&lt;pre class=&quot;language-groovy&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;dependencies &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    compile &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.jetbrains.kotlin:kotlin-stdlib-jdk8:&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;kotlin_version&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    compile group&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.http4k&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http4k-core&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3.113.0&quot;&lt;/span&gt;&lt;/span&gt;
    compile group&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;org.http4k&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http4k-server-netty&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;3.113.0&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will add the base http4k library and the netty server library, which these days is my underlying server of choice when building JVM microservices. However, http4k supports various other server implementation as well such as Jetty, Apache Tomcat etc. and some other prominent jvm web servers.&lt;/p&gt;
&lt;p&gt;First thing I tried to do was to experiment setting up a simple http server, this is where http4k is a joy to work with as a library.&lt;/p&gt;
&lt;p&gt;Setting up a basic server is fairly simple and requires almost no effort.&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/alive&quot;&lt;/span&gt;&lt;/span&gt; bind GET &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;The crew is more kahless now than vogon. biological and tightly dead.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/api&quot;&lt;/span&gt;&lt;/span&gt; bind &lt;span class=&quot;token function&quot;&gt;routes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/embedLink&quot;&lt;/span&gt;&lt;/span&gt; bind GET &lt;span class=&quot;token keyword&quot;&gt;to&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;  &lt;span class=&quot;token function&quot;&gt;Response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;OK&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getOembedData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;link&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; nettyServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;asServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Netty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The snippet above starts up a basic Netty server on port 9000. The startup time is very small, I calculated less than &amp;lt;2 seconds in my observations but the tests were not rigorous so take it with a grain of salt. Once started, the app will start serving the two routes &lt;code&gt;/alive&lt;/code&gt; and &lt;code&gt;/api&lt;/code&gt; on the local machine. the alive endpoint is simple to make sure we can ping the app so I;m not going to talk about it anymore. The &lt;code&gt;/api&lt;/code&gt; link however has an embedded route to &lt;code&gt;/embedLink&lt;/code&gt; which allows us to make &lt;em&gt;GET&lt;/em&gt; calls to the server with the query param link which contains the link to the resource we are wanting to get the oembed link for.&lt;/p&gt;
&lt;p&gt;So, let&#39;s talk a little bit more about how we do that...&lt;/p&gt;
&lt;h2 id=&quot;extracting-oembed-data&quot; tabindex=&quot;-1&quot;&gt;Extracting Oembed data &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/simple-oembed-service-using-http4k/#extracting-oembed-data&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now oembed as a protocol is fairly old and many services directly support getting oembed data for the content on their sites using public APIs. One of such sites is &lt;a href=&quot;https://www.instagram.com/&quot;&gt;Instagram&lt;/a&gt; which has a nice api that properly adheres to oembed standards. Without going to deep into the oembed specification (which if you&#39;re keen you can read more about &lt;a href=&quot;https://oembed.com/#section2&quot;&gt;here&lt;/a&gt;), we&#39;ll see how we use the powerful lensing capability of http4k modules to extract oembed data.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;getOembedData()&lt;/code&gt; function from the routes code snipper in the previous section can be implemented as:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getOembedData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;link&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Method&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;GET&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://api.instagram.com/oembed/?url=&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token expression&quot;&gt;link&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; HttpHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JavaHttpClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; igLens &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;auto&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;IgOembedResponse&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toLens&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; igLens&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;html
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we provide the function an actual link to the resource (in this case a link to a post instagram) and use the hardcoded instagram oembed url to grab the oembed html.&lt;/p&gt;
&lt;p&gt;To actually parse the returned response from instagram, we use a very nifty feature in http4k called &lt;a href=&quot;https://www.http4k.org/cookbook/typesafe_http_requests_with_lenses/&quot;&gt;Lenses&lt;/a&gt; which allows us to use typesafety while working with the request/response from the http client calls. Lenses are a very powerful feature and allow not only immutable parsing of request/response objects but also use ADTs to parse the responses in an &lt;a href=&quot;https://en.wikipedia.org/wiki/Monad_(functional_programming)#An_example:_Maybe&quot;&gt;Maybe&lt;/a&gt; using functional extension libraries for kotlin such as &lt;a href=&quot;https://arrow-kt.io/&quot;&gt;Arrow&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To actually extract the data, we have to define the type of the returned response first and nothing better to do that, than the ever so simple kotlin data classes:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@JsonIgnoreProperties&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ignoreUnknown &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;IgOembedResponse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; version&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; title&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author_name&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; authorName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author_url&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; authorUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;author_id&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; authorId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Long&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;media_id&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; mediaId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;provider_name&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; providerName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;provider_url&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; providerUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; width&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; height&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Int&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; html&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;thumbnail_url&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; thumbnailUrl&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;thumbnail_width&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; thumbnailWidth&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token annotation builtin&quot;&gt;@JsonAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;thumbnail_height&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; thumbnailHeight&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, I&#39;ve used the &lt;code&gt;@JsonIgnoreProperties&lt;/code&gt; and &lt;code&gt;@JsonAlias&lt;/code&gt; annotations from &lt;a href=&quot;https://github.com/FasterXML/jackson&quot;&gt;jackson&lt;/a&gt; which is a well known serialization/deserialization library for java. However, http4k does not limit to just one such library and provides various options such as &lt;a href=&quot;https://github.com/google/gson&quot;&gt;Gson&lt;/a&gt;, &lt;a href=&quot;https://github.com/square/moshi&quot;&gt;moshi&lt;/a&gt; etc. as plugins. Just remember to add the library of your choice in your build.gradle.&lt;/p&gt;
&lt;p&gt;Finally, going back to our code snippet for &lt;code&gt;getOembedData&lt;/code&gt; introduced earlier, we first setup a new client:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; HttpHandler &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;JavaHttpClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which gives us a new Java based http client, which is a simple implementation of a simple Request -&amp;gt; Response client as a function introduced in the [previous section](## Setting up a micro server with http4k). All it does is take a request and parses the response as a bytestream.&lt;/p&gt;
&lt;p&gt;To parse the bytestream in a typesafe way, we attach it to our lens which contains the data class we just defined:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;igLens&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extract&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;if the request is successful, we will get an object parsed into the &lt;code&gt;IgOembedResponse&lt;/code&gt; type. For our purposes, what we really need is the html, which gives us the full oembed html which we can use in an iframe.&lt;/p&gt;
&lt;h3 id=&quot;final-notes&quot; tabindex=&quot;-1&quot;&gt;Final Notes &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/simple-oembed-service-using-http4k/#final-notes&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the end, I ended up deploying the app on aws lambda by by setting up an API gateway and a lambda function that called the final app. The final cleaned source code for the app is located &lt;a href=&quot;https://github.com/shavz/koember&quot;&gt;here&lt;/a&gt; and it also contains the instructions on how to call the aws lambda function to get the oembed responses.&lt;/p&gt;
&lt;p&gt;All in all, I was pleasantly surprised at how productive I was working with http4k and how easy it is to set up a FAAS type application using kotlin and AWS.&lt;/p&gt;
</content>
	</entry>
	
	<entry>
		<title>Post Boot Actions Using Application Events</title>
		<link href="https://shiveenp.com/blog/post-boot-actions-using-application-events/"/>
		<updated>2019-01-10T07:23:18Z</updated>
		<id>https://shiveenp.com/blog/post-boot-actions-using-application-events/</id>
		<content type="html">&lt;h2 id=&quot;background&quot; tabindex=&quot;-1&quot;&gt;Background &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/post-boot-actions-using-application-events/#background&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Recently I faced an interesting problem at work. A production app written in Kotlin with Spring Webflux as the webframework had suddenly started taking more than 189 seconds on average to start (more than 3 minutes 😱).&lt;/p&gt;
&lt;p&gt;Now as much as I love spring for the easy to use and battle tested APIs, everyone knows its not the nimblest of frameworks around, but having a boot time of 3 minutes was simply unacceptable. The problem was made even worse by the fact that our production kubernetes cluster, on which the app was deployed, usually checks if the app is alive (a simple alive endpoint on the REST api) and triggers container restart if the app doesn&#39;t respond withing a given time threshold. Due the delay in the app boot, the kubernetes kept on assuming the app has not started yet and it triggered a flurry of container restarts - resulting in our very talented and very hard working support team being pinged at ungodly hours. Something needed to be done...&lt;/p&gt;
&lt;p&gt;After spending some time analyzing the application, we found that most of the time was being spent in the startup for &lt;a href=&quot;https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/transport-client.html&quot;&gt;Elastic Search Transport client&lt;/a&gt;, which loads up several plugins, one of which is the &lt;code&gt;Transport Client&lt;/code&gt; which relies on the negotiating underlying netty thread which also happens to be the same underlying server that spring webflux uses.&lt;/p&gt;
&lt;p&gt;To test the theory that this was the reason the build was slowing down, we commented out the code that builds up the client and noticed that the app was starting in less than 10 seconds now! WOAH! that&#39;s a big gain...&lt;/p&gt;
&lt;h2 id=&quot;setting-up-post-bootup-logic&quot; tabindex=&quot;-1&quot;&gt;Setting up post bootup logic &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/post-boot-actions-using-application-events/#setting-up-post-bootup-logic&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://spring.io/&quot;&gt;Spring&lt;/a&gt; always has had events inside the system since the very beginning as a loose way to exchange application context information inside a running spring app and there are variety uses, most frequently being the ability to detect any local or global event change and then associating actions or events that take place post the event taking place.&lt;/p&gt;
&lt;p&gt;To solve out problem, we created a class called event listener where all the startup logic was shifted (the code is in &lt;a href=&quot;https://kotlinlang.org/&quot;&gt;Kotlin&lt;/a&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; EventListener &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expensiveThings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;//        do expensive intialization steps here&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@EventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ApplicationReadyEvent&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bootStrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;expensiveThings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code above is very simple, it sets up a class called event listener which will contain all our spring application event listener logic. We have a function named &lt;code&gt;expensiveThings()&lt;/code&gt; , which does something that involves heavy initialization (think network negotiation, I/O bound work etc.) steps and something we want to do pot application startup. Ideal candidates for this scenario might be plugins or utilities that let you interact with third party services like elastic search but that are not absolutely necessary to have when your app is first starting to serve it&#39;s controller endpoints. Inside &lt;code&gt;expensiveThings()&lt;/code&gt; we can add something with heavy init. Function &lt;code&gt;bootStrap()&lt;/code&gt; is where all the magic takes place. We use the spring &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/event/EventListener.html&quot;&gt;EventListener&lt;/a&gt; which gives us the ability to link any &lt;code&gt;void&lt;/code&gt; returning function to an application event, in other words, at runtime spring will proxy the call for that event to be triggered when the registered event is received inside the springboot application context. In our case that is the springs &lt;strong&gt;ApplicationReadyEvent&lt;/strong&gt; which gets sent out the first time a spring boot application fully starts up.&lt;/p&gt;
&lt;p&gt;But our job is not done yet, we would like a way to access the initialized variable throughout our application, and we would like to do it in as idiomatic spring way as possible.&lt;/p&gt;
&lt;p&gt;To achieve that we will modify our previous code as follows:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; EventListener &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; poststartupVar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expensiveThings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        poststartupVar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this is just an example&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@EventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ApplicationReadyEvent&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bootStrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;expensiveThings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;here we&#39;ve modified the class to include a &lt;code&gt;lateinit&lt;/code&gt; (if you don&#39;t know, a &lt;a href=&quot;https://kotlinlang.org/docs/reference/properties.html#late-initialized-properties-and-variables&quot;&gt;lateinit&lt;/a&gt; variable in kotlin is simply a variable that doesn&#39;t need to be initialized at declaration, this makes the code cleaner but you run the risk of an ugly exception if the variable is still initialized at the time of accessing); which then gets instantiated in expensive things. We&#39;ve also marked the class as a configuration object, which makes it a candidate for injection autowring througghout your spring managed beans like Components, Services etc.&lt;/p&gt;
&lt;p&gt;This should get you up an running with a variable that gets initialized post the ApplicationReadyEvent which, if your application has REST controllers, happens post the app starting to listen on those endpoints.&lt;/p&gt;
&lt;p&gt;However, what if this component is a core component and we want to hard exit as soon as possible.&lt;/p&gt;
&lt;p&gt;No problem, as demonstrated below:&lt;/p&gt;
&lt;pre class=&quot;language-kotlin&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-kotlin&quot;&gt;&lt;span class=&quot;token annotation builtin&quot;&gt;@Configuration&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; EventListener&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationContextAware &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;lateinit&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; poststartupVar&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; context&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ApplicationContext&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expensiveThings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        poststartupVar &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string-literal singleline&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this is just an example&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token annotation builtin&quot;&gt;@EventListener&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ApplicationReadyEvent&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;bootStrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;expensiveThings&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ex&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fun&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;panic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;val&lt;/span&gt; containerContext &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;context &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; ConfigurableApplicationContext
        SpringApplication&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;containerContext&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we use the help of &lt;a href=&quot;https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html&quot;&gt;ApplicationContext&lt;/a&gt; to inject the current running springboot application context in the even listener configuration (more details &lt;a href=&quot;https://spring.io/understanding/application-context&quot;&gt;here&lt;/a&gt;). In case an exception is encountered in the &lt;code&gt;bootStrap()&lt;/code&gt;, the &lt;code&gt;panic()&lt;/code&gt; function will be invoked and the spring application will be gracefully exited (the graceful bit depends on your configuration but that&#39;s for another blogpost).&lt;/p&gt;
&lt;h2 id=&quot;final-notes&quot; tabindex=&quot;-1&quot;&gt;Final notes &lt;a class=&quot;header-anchor&quot; href=&quot;https://shiveenp.com/blog/post-boot-actions-using-application-events/#final-notes&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A code very similar to that with some business speicifc sauce is what I used to solve the problem we encountered in the previous section and it brought down the application startup time to less than 15 seconds, which was a major win for a critical production application. Utilizing the APIs and utilities provided by Spring out of the box in my opinion provides the easiest and most convenient way to solve most production problems and is a testament to maturity of Spring and its status as one of the top jvm web framework out there.&lt;/p&gt;
</content>
	</entry>
</feed>
