Webhooks is ideal in situations where you need to get notified of certain events like an email being opened, a contact getting updated or a calendar event getting deleted. While you need an external server to host your webhook-enabled application, thanks to the Nylas Java SDK, you can easily test webhooks locally on your machine.
As we know, we can use a web server to test our webhooks as we demonstrated using Ruby, Python, and PHP , we can test webhooks locally by using the Nylas CLI.
This of course brings us to our main point of showing how to use another one of our SDKs to test webhooks locally, the Java SDK.
Why test Webhooks locally
Well, anything that we can test on our local machine is better, because if anything goes wrong, we can always quickly fix it. Having to test on a web server makes it more complex as we need sometimes to repeat a long workflow to submit all the needed changes.
By testing locally, we can make sure everything is working properly before we move it to an external web server.
What does our webhooks application look like?
We’re going to create a SparkJava application, which is a web application but also we’re going to use Twilio to be able to send ourselves an SMS message when a certain event happens, like when someone opens a link that we sent.
Let’s zoom in to showcase that we’re getting information on who’s sending the email, who is receiving the email, and what type or kind of Webhook we’re getting.
When the recipient opens the link that we send inside an email, we will get notified via SMS:
Let’s recap the information we’re getting:
Id → The message id. This will help us to retrieve the message information.
Date → Date and time when the webhook notification gets generated.
Email From → Who is sending the message
Email To → Who is receiving the message
Title → Subject of the message
Type → Type of Webhook, created, updated or opened.
Is your system ready?
If you already have the Nylas Java SDK installed and your Java environment is configured, then continue along with the blog.
In order to get SMS delivered to our cell phones, we need to create a Twilio account:
After that, you will need to create a Twilio Phone number from where the SMS will be sent:
Don’t forget to copy the Account SID, Auth Token and Twilio phone number and store them in your .env file:
Creating an email tracking application
Our Webhooks application is going to deal with two different kinds of emails, one tracked and the other untracked. The first one needs to be sent using some extra commands, so we’re going to create a small program for that, the second one can be sent like any regular email.
Let’s create a new project called Email_Tracking with a main class called emailtracking.java.
Keep in mind that Email Tracking is a paid customer feature, so with a trail account, you will not be able to use it.
Our project is going to be called Local_Webhooks, and it will have a main class called localwebhooks. We will need to change the default pom.xml file and create one Mustache template called main.
As we mentioned, we want to use a web framework, and a very light and fast option is SparkJava.
Creating the pom.xml file
We’re going to include some useful libraries:
spark-core → The Spark Web Framework
nylas-java-sdk → The Nylas Java SDK
dotenv-java → To enable using .env files
spark-template-mustache → To allow mustache on Spark projects
twilio → To deal with SMS
slf4j-api → To handle loggingslf4j-simple → To handle logging
With our pom.xml file ready, we can move on to the source code.
Creating the source code
Here’s the source code of our localwebhooks.java class:
//Import Java Utilities
import java.net.URISyntaxException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.*;
import java.io.IOException;
//Import Nylas Packages
import com.nylas.*;
import com.nylas.services.Tunnel;
//Import DotEnv to handle .env files
import io.github.cdimascio.dotenv.Dotenv;
// Import Spark and Mustache libraries
import spark.ModelAndView;
import static spark.Spark.*;
import spark.template.mustache.MustacheTemplateEngine;
// Import Twilio libraries
import com.twilio.Twilio;
import com.twilio.rest.api.v2010.account.Message;
public class localwebhooks {
static Dotenv dotenv = Dotenv.load();
private static String id;
private static String type;
// Get Twilio keys
public static final String ACCOUNT_SID = dotenv.get("TWILIO_SID");
public static final String AUTH_TOKEN = dotenv.get("TWILIO_TOKEN");
public static void main(String[] args) throws RequestFailedException,
IOException, URISyntaxException {
// Initialize Twilio client
Twilio.init(ACCOUNT_SID, AUTH_TOKEN);
// List to hold webhooks notifications
List<WebhookInfo> webhook_list = new ArrayList<>();
// Create the Nylas client object
NylasClient client = new NylasClient();
// Create the Nylas account object
NylasAccount account = client.account(dotenv.get("ACCESS_TOKEN"));
// Connect it to Nylas using the Access Token from the .env file
NylasApplication application = client.application(dotenv.get("CLIENT_ID"),
dotenv.get("CLIENT_SECRET"));
// Create a Webhook tunnel connection, passing the required triggers
Tunnel webhookTunnel = new Tunnel.Builder(application, new HandleNotifications()).
triggers(Webhook.Trigger.MessageCreated,
Webhook.Trigger.MessageOpened,
Webhook.Trigger.MessageUpdated,
Webhook.Trigger.MessageLinkClicked).build();
// Establish the Webhook connection
webhookTunnel.connect();
// Default path when we load our web application
get("/", (request, response) -> {
// Create the Nylas messages endpoint
Messages messages = account.messages();
// If we get the message id
if(id != "" && id != null){
// Create a new webhookinfo structure object
WebhookInfo info = new WebhookInfo();
// Retrive the information from the message
com.nylas.Message message = messages.get(id);
// Convert the date into something readable
LocalDateTime ldt = LocalDateTime.ofInstant(message.getDate(),
ZoneOffset.UTC);
String date = ldt.getYear() + "/" + ldt.getMonthValue() + "/" +
ldt.getDayOfMonth();
String time = ldt.getHour() + ":" + ldt.getMinute() + ":" + ldt.getSecond();
// Fill up the webhookinfo structure
info.id = message.getId();
info.date = date + " on " + time;
info.emailFrom = message.getFrom().get(0).getEmail();
info.emailTo = message.getTo().get(0).getEmail();
info.title = message.getSubject();
info.type = type;
// Add the structure to the list of webhooks notifications
webhook_list.add(info);
// If someone click on the link in the email
if(type.equals("message.link_clicked")){
// Send a Twilio SMS
Message twilio_message = Message.creator(
new com.twilio.type.PhoneNumber("+13439969736"),
new com.twilio.type.PhoneNumber(dotenv.get("TWILIO_PHONE")),
"The link you sent to " + message.getTo().get(0).getEmail() +
" was opened").create();
// Trigger the SMS
System.out.println(twilio_message.getSid());
}
}
id = "";
// Create a model to pass information to the mustache template
Map<String, Object> model = new HashMap<>();
model.put("webhook_list", webhook_list);
// Call the mustache template
return new ModelAndView(model, "main.mustache");
}, new MustacheTemplateEngine());
}
// Setters and Getters for Id and Type
public static void set_Id(String sId){
id = sId;
}
public static String get_Id(){
return id;
}
public static void set_Type(String sType){
type = sType;
}
public static String get_Type(){
return type;
}
}
// Structure to hold the Webhooks notification information
class WebhookInfo {
String id;
String date;
String emailFrom;
String emailTo;
String title;
String type;
}
// Every time there's a new Webhook, we're going to deal with it here
class HandleNotifications implements Tunnel.WebhookHandler {
@Override
public void onMessage(Notification.Delta delta) {
Dotenv dotenv = Dotenv.load();
localwebhooks local = new localwebhooks();
String type = delta.getTrigger();
// Get the Id for either message.created or message.updated
if(type.equals("message.created") || type.equals("message.updated")) {
local.set_Id(delta.getObjectData().getId());
// Get the Id for either message.opened or message.link_clicked
}else{
local.set_Id(delta.getObjectData().getMessageTrackingData().getMessageId());
}
local.set_Type(type);
}
// When the connection to the Webhook tunnel is made
@Override
public void onOpen(short httpStatusCode) {
System.out.println("Webhook tunnel is open");
}
}
Inside the resources folder, we need to create a templates folder and inside we need to create one file called main.mustache: