Yogesh Dhimate Notes to Myself

Random Act of Kindness

I frequently visit one online group. It sends some thought-provoking prompts to reflect on past experiences and random ideas.

Here are some examples.

  • Which bad commercial that you saw that has stayed with you?
  • What does a ‘perfect day’ mean to you?

This week’s prompt was about the act of kindness shown to you, that you’ll never forget.

I wanted to share such experience.

Before COVID-19, I was traveling for my job. In July-August 2019, my assignment was in Moline, IL. It’s a small town on Illinois - Iowa border. This was my very first visit to the city. Due to an inclement weather-induced delay, I landed at Moline airport around 12.50 AM on Monday. I couldn’t find an Uber or a taxi to go to the hotel. Apparently no one provides a ride at that time of the night. I didn’t have a rental car booking, and the rental car counters couldn’t provide me a car on the spot. My only other option was to wait at the airport for the next 5 hours and hope for the cab driver to show up. There was no food at that small airport. I was more worried about my important meeting in the morning. As a last attempt, I called the hotel - Element - to check on the possibility of helping me out. I was aware that they don’t provide an Airport shuttle. Fortunately, the front desk lady at the hotel was super helpful and drove at 1.30 AM in her own car to pick me up at the airport.

This random act of kindness from a stranger has left a lasting impression on me. It has also associated this experience with Mariott - the owners of the Element hotels. Whenever I book a hotel room, I search for availability at one of the Mariott’s hotels first.

My Audible Experience

Introduction

Last month I signed up for Audible. I thought it would be great to listen to the audiobooks, instead of reading them. In the past, I had enjoyed a few audiobooks on my long road trips. I especially remember listening to George Carlin’s books. The idea of listening to books on the go, the ultimate portability, and convenience, and an opportunity to give rest to my eyes was very enticing. Audible was offering a 1-month free trial. I skimmed their huge catalog and signed up. The signup process was smooth - as you would expect from Amazon. I downloaded The Spy and the Traitor by Ben Macintyre and started listening to it. While the book was great, one of the best thrillers I enjoyed, my experience with Audible was suboptimal.

Challenges with Audiobooks:

  1. Audiobooks feel strange: When reading a physical or ebook, I feel like driving in daylight, with clear visibility. I can look in the rearview mirrors or outside the windows. I can notice another car approaching the intersection. If someone is overtaking I can adjust my driving. With audiobooks, I feel like someone else is driving even though I am in the driver’s seat. We are traveling in the dark with only front lights. I can’t look sideways or behind, as its all dark
  2. Audiobooks aren’t as convenient as I would like them to be: Audiobooks need a lot of concentration. It’s very easy to get distracted, tune out and miss the important part. Once you lose focus, it becomes very difficult to get back on track.
  3. Audiobooks don’t give the satisfaction of reading: Almost always the narrator is different. His performance is very clean and robotic. There is a constant feeling that it’s not me who is reading this book.

Issues with Audible

  1. Audible’s subscription: When I signed up for Audiobook’s trial, I thought it was like a Netflix model. I was hoping to get access to a large library of content for a low monthly price. I misunderstood. With Audible I get 1 credit to buy a book of my choice every month. Credits expire and there is pressure to buy the book to use those credits. I can buy most of the books I want, whenever I want, without an Audible subscription and still save some money.
  2. Problems with the Audible app - Audible books have DRM, so I can’t listen in the app I like. I have to use the Audible app, which is okay for the most part. But my phone’s aggressive battery management policy kills the app if I pause the book. This is super inconvenient. To begin listening again, I have to restart the app.
  3. Better and cheaper options available - I can borrow audiobooks for free from my local library. Their catalog is not as big as Audible’s but only 2 books I wanted to read were unavailable in my library.

I don’t think I will continue my Audible subscription for now.

Certificate of Authenticity

This week I got my new pair of glasses. I use photochromic lenses to help protect my eyes from the bright sunlight. These lenses turn dark when exposed to sunlight or high temperature. With this pair, my optician gave me a ‘Certificate of Authenticity’ card from this company called ‘Transitions’. Transitions provide the photochromic feature in the lenses. I wasn’t aware of them. Anyway, this certificate requested that I should register my purchase. I can’t figure out why I should do this, and what’s the downside of not registering. This also got me confused on many fronts. My last 4 pairs purchased from the same optician had photochromic lenses. Yet, I never got a certificate of authenticity. Does it mean those were inauthentic photochromic lenses? What would happen if I continue wearing them? Is this a new program to certify authentic lenses? Is there a spread of inauthentic lenses that the company is trying to contain?

Whatever it might be. I am curious to understand how did this ‘Certificate of Authenticity’ program launch? What’s the strategy? What could be the end goal? Is it to increase brand awareness? That is one possibility. Even though my last 4 pairs had photochromic lenses, I never asked for a specific brand. Now I know about a product, which guarantees the authenticity of photochromic lenses. The next time I am ready for new glasses, should I insist on this specific brand? It is authentic. I know now! Or is it a desperate attempt to capture my contact information? So they can send me targeted advertisements? I looked at the registration page. It asked for my name, address, email, date of birth, gender(!), and other information. At least they didn’t ask for my phone number! (Do they already have it?)

Another interesting question is, how does it relate to Crizal - my go-to lens brand? My lense says it’s from Crizal! Then why do I have this certificate from Transitions? My cursory check on the internet shows that these are two different companies. Does Crizal provide the physical lens? And Transitions provide some sort of mechanism (sticker or a chemical?) to turn the lense dark when exposed to sunlight? Or is it the other way round where Transitions provide the color-changing physical lense, and Crizal provides an anti-reflective, anti-scratch coating on it?

This innocent little card has raised a lot of questions in my mind.

February Quick Update

I haven’t posted anything in February. It was the busiest month I ever had. Our baby girl decided to arrive earlier than expected. Due to the pandemic, we decided to manage baby things on our own, without our parents. It’s fun, but for the first time parents like us, it’s not easy. The most difficult part is baby can’t talk and exactly tell us what she wants yet. So we have to decipher her crying. Decoding her crying is like solving a puzzle. Is the diper wet, or poopy? Is she hungry? Or too full and needs to burp? But we just burped few mins ago. Is she too cold or too warm? Or just plain bored? The first few weeks I took a brute force approach and tried everything to soothe her. It worked but it was very inefficient (and exhausting). After a couple of more weeks, we got a hang of it and somewhat able to understand what she wants. (or she has given up on our parenting :)). The interesting thing about parenting is that everyone else has an opinion, and it usually comes down to how we are doing it wrong. ;) I learned to ignore it for the most part. I think nobody knows what they are doing, so it’s just figuring out on the go.

Anyways, here is a funny anecdote. We were visiting the doctor for regular checkups over the last year. On our first visit to the hospital, the nurse told us to ‘go downstairs to the lab’ for the blood checkup. Downstairs I noticed something interesting. There was no lab to be found. At least not easily. There were at least 4 other offices, each of them telling me that ‘it’s not a lab’. The notice on one door told me that the lab was down the hall. But the Y shaped hallway had 3 corners to go.

After walking across all the hallways on that floor for 8-10 minutes, we finally located the lab in one remote corner. I guess we were just unlucky to pick the wrong hallways first. This bad ‘UX’ could’ve been easily avoided by simply putting a small arrow in the direction of the lab.

Whenever I am near the hospital, I feel I should go inside the building and put an arrow to show where the lab is.

Using Epic's FHIR APIs in Mule 4

Introduction

FHIR (Fast Healthcare Interoperability Resources) is a new and emerging standard for interoperability in healthcare IT. Unlike the ancient HL7v2 standard, FHIR is open, extensible, and composable. It supports modern web standards like JSON, XML, HTTP(!), and Turtle. It also defines different data exchange methods like RESTful API, messaging, services, documents, and persistent storage. Best of all the FHIR documentation is outstanding: There is detail documentation for every type of resource, with lots of examples. Enough detail for anyone to create a compliant implementation. All of the data types like dates and times, terminologies, identifiers, references, numeric values, extensions are documented in detail, including how to read and write them. It’s probably one of the best documented and well thought out APIs I have worked with.

Epic made their FHIR APIs available on https://fhir.epic.com/. In this post, we will discuss using Epic’s FHIR APIs in Mule 4

Prerequisites

You will need an Epic developer account. You can get one for free by signing up here. I have used Anypoint Studio 7.7 for Mule app.

Configuration

Certificate setup for JWS

  1. We will use SMART Backend Services approach described here which uses signed JWT to get the access token.

  2. Generate a new public-private key pair, and get the certificate(s) in appropriate format

generate a new public-private key pair

openssl genrsa -out Anypoint_Mulesoft.pem 2048

export the public key to a base64 encoded X.509 certificate

openssl req -new -x509 -key Anypoint_Mulesoft.pem -out Anypoint_PublicKey.pem -subj ‘/CN=Anypoint’ -days 365

export the keystore in pkcs12

openssl pkcs12 -export -in Anypoint_PublicKey.pem -inkey Anypoint_Mulesoft.pem -out Anypoint_Keystore.p12

Create an app in Epic

  1. Login to https://fhir.epic.com/ and click on ‘Build Apps’

  2. Click on ‘Create’ and enter your app details as follows. Make sure to select the Application Audience as ‘Backend Systems’. For Sandbox JWT signing public key, upload the public key store (Anypoint_PublicKey.pem) file generated in the previous step. Click on save.

  3. You should see the Client ID and Non-Production Client ID for your app. Select the SMART on FHIR version as R4, enter the summary, accept the terms of usage and click on ‘Save & Ready for Sandbox’.

In my experience it usually takes a couple of hours for your app to be usable in Epic. It took me ~2 hours for my app to be usable

Mule application details

  1. Create a new Mule application, and copy the pkcs12 formatted keystore (Anypoint_Keystore.p12) created earlier, in the src/main/resources folder.

  2. To generate a signed JWT (JWS), we will use Java JWT library. In the pom.xml of your Mule application, include the following dependencies. (I have included Apache log4j dependencies, these are optional).

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.14.0</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.14.0</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId> 
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.65</version>
    <type>jar</type>
</dependency>

  1. In Mule application’s src/main/java folder, create a new Java package com.dhimate.demo, class JWTProvider and use following code snippet to generate signed JWT.
package com.dhimate.demo;

import io.jsonwebtoken.Jwts; 

import java.io.File;
import java.io.FileInputStream;
import java.net.URL;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.util.Date;
import java.util.Enumeration;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


public class JWTProvider {
//	private static final String PRIVATE_KEY_FILE_RSA = "Anypoint_Keystore.p12";
//	private static final String PRIVATE_KEY_PASSWORD = "CHANGEIT";
//	private static final String EPIC_CLIENT_ID = "86121d55-247e-4a0b-b3c0-6fa28c995f72";
//	private static final String EPIC_FHIR_URL = "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token";
	
	private static final Logger logger = LogManager.getLogger("JWTProvider");
	public static String getToken(String PRIVATE_KEY_FILE_RSA, 
                                String PRIVATE_KEY_PASSWORD, 
                                String EPIC_CLIENT_ID, 
                                String EPIC_FHIR_URL) throws Exception {
		
		logger.info("Generating JWS token");
		
		String jws = Jwts
				.builder()
				.setHeaderParam("alg", "RS384")
				.setHeaderParam("typ", "JWT")
				.setIssuer(EPIC_CLIENT_ID)
				.setSubject(EPIC_CLIENT_ID)
				.setAudience(EPIC_FHIR_URL)
				.setId(UUID.randomUUID().toString())
				.setExpiration(new Date(System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(5)))
				.signWith(getKey(PRIVATE_KEY_FILE_RSA, PRIVATE_KEY_PASSWORD))
				.compact();
		
		return (jws);
	}

	private static PrivateKey getKey(String PRIVATE_KEY_FILE_RSA, 
                                    String PRIVATE_KEY_PASSWORD) throws Exception {
		
		KeyStore keystore = KeyStore.getInstance("PKCS12");
		URL resource = JWTProvider.class.getClassLoader().getResource(PRIVATE_KEY_FILE_RSA);
		FileInputStream is = new FileInputStream(new File(resource.toURI()));
		
		keystore.load(is, PRIVATE_KEY_PASSWORD.toCharArray());
		Enumeration<String> aliases = keystore.aliases();
		String keyAlias = "";
		
		while (aliases.hasMoreElements()) {
			keyAlias = (String) aliases.nextElement();
		}

		PrivateKey key = (PrivateKey) keystore.getKey(keyAlias, PRIVATE_KEY_PASSWORD.toCharArray());
		return (key);
	}
}
 

Your Mule application should look like this.

  1. In Mule application’s src/main/java folder, create a new Java package com.dhimate.demo, class JWTProvider and use the following code snippet to generate signed JWT.

  2. Calling Epic’s FHIR APIs is a two-step process.
    • In the first step we need to get the access token using the signed JWT token
    • In the second step we will use the access token received in the step above to access FHIR resource
  3. Let’s look at the configuration of important components
    • Generate signed JWS token using Invoke Static component
    • Prepare request payload to get access token
    • Get the access token
    • Call FHIR API to get the data
  4. Once this is done, we can add a simple HTTP listener and connect our flow to it to get the data from Epic’s FHIR API. Epic has provided test data in the sandbox. Let’s use it to search for a patient record.