<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrey Vishin</title>
    <description>The latest articles on DEV Community by Andrey Vishin (@andreyvishin).</description>
    <link>https://dev.to/andreyvishin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1012064%2F964aa69e-ad04-4af1-b835-5e3aca705a0a.jpeg</url>
      <title>DEV Community: Andrey Vishin</title>
      <link>https://dev.to/andreyvishin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/andreyvishin"/>
    <language>en</language>
    <item>
      <title>The bugs that look like success: quality checks for AI-generated learning content</title>
      <dc:creator>Andrey Vishin</dc:creator>
      <pubDate>Thu, 22 Oct 2026 15:37:44 +0000</pubDate>
      <link>https://dev.to/andreyvishin/the-bugs-that-look-like-success-quality-checks-for-ai-generated-learning-content-3h61</link>
      <guid>https://dev.to/andreyvishin/the-bugs-that-look-like-success-quality-checks-for-ai-generated-learning-content-3h61</guid>
      <description>&lt;h2&gt;
  
  
  The card that passed every check
&lt;/h2&gt;

&lt;p&gt;During the final end-to-end test before LAFwords launched, I tried a deliberately broken input. I typed &lt;code&gt;asdfgh&lt;/code&gt; as a word to learn, and when the app asked whether I meant something else, I chose to keep my original word.&lt;/p&gt;

&lt;p&gt;The app built a card anyway. It had ten example sentences, an AI image, pronunciation audio and a success dialog, and it went into my study queue. Here are some of the sentences, with the word the card was teaching in bold:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"I'm sorry, but &lt;strong&gt;asdfgh&lt;/strong&gt; isn't a recognized English word, so I can't create valid usage samples for it."&lt;/li&gt;
&lt;li&gt;"&lt;strong&gt;Please&lt;/strong&gt; provide a real English word so I can generate varied usage samples."&lt;/li&gt;
&lt;li&gt;"&lt;strong&gt;Tell&lt;/strong&gt; me a real target word and I will produce ten diverse sentences."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every sentence was a piece of the model's apology. And every automated check had passed. All the words were real dictionary words. The entered word appeared in a sentence. There were enough sentences.&lt;/p&gt;

&lt;p&gt;That bug is the best summary I have of what testing LLM output is like. The dangerous failures don't look like failures. They look like success.&lt;/p&gt;

&lt;p&gt;This post is about how I check the content LAFwords generates: what broke, what each break taught me, and what my checks still don't catch. My background is in software QA, with hands-on work in prompt engineering and in evaluating non-deterministic LLM output. This has been the most interesting testing problem I've worked on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets generated, and why errors cost more here
&lt;/h2&gt;

&lt;p&gt;LAFwords is a vocabulary app for intermediate and advanced learners. You enter a word you met, in English or Spanish, and the app builds a card: ten sentences that show the word in different situations, each with a short meaning, a context label and an image, plus native pronunciation. There are no translations. Ten varied contexts give you a feel for the word's range, so you can use it in situations the ten sentences never showed.&lt;/p&gt;

&lt;p&gt;That design allows no compromise on quality. A learner can't spot the errors because they don't know the word yet. If a sentence uses the word in the wrong sense, or invents a form that doesn't exist, the learner memorizes the mistake. The generated content is the curriculum.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The model can't check its own inventions
&lt;/h2&gt;

&lt;p&gt;It gets worse: language models are prone to inventing word forms. They apply patterns like -ity and -ness that work for thousands of words, but real vocabulary doesn't follow them everywhere—borrowed words and one-off formations break the rules.&lt;/p&gt;

&lt;p&gt;Early on, the model produced &lt;em&gt;holisticity&lt;/em&gt; (for &lt;em&gt;holistic&lt;/em&gt;) and &lt;em&gt;rigorousnesses&lt;/em&gt; (for &lt;em&gt;rigorous&lt;/em&gt;). Neither is an English word. Both look plausible, which is the problem.&lt;/p&gt;

&lt;p&gt;The obvious idea is: let's ask the model whether a word is real! It doesn't work reliably. The reason is simple: we judge the invented word using the same model knowledge that generated it. A checker that shares the generator's blind spot passes exactly the errors you most need to catch.&lt;/p&gt;

&lt;p&gt;So the first lesson I learned: don't let the model check itself. The fabrication check doesn't use an LLM at all. Every word in every sentence is checked against real spelling dictionaries, the same kind a word processor uses. The dictionary brings ground truth from outside the model. If a sentence contains a word the dictionary doesn't know, it's rejected and regenerated.&lt;/p&gt;

&lt;p&gt;A side lesson came from the re-test. The model used &lt;em&gt;rigorousness&lt;/em&gt;, and my first reaction was that it was another invention. It isn't. It's in the dictionary. My instinct as a reviewer was no better than the model's, and only the external source could decide.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The quality rule that caused the defect
&lt;/h2&gt;

&lt;p&gt;Why was the model inventing words in the first place? Because I asked it to.&lt;/p&gt;

&lt;p&gt;The early prompt required several different forms of the word across the ten sentences, so the learner would see &lt;em&gt;decide&lt;/em&gt;, &lt;em&gt;decided&lt;/em&gt;, &lt;em&gt;deciding&lt;/em&gt;, &lt;em&gt;decides&lt;/em&gt;. It sounded like good teaching. But &lt;em&gt;holistic&lt;/em&gt; has only two real forms: &lt;em&gt;holistic&lt;/em&gt; and &lt;em&gt;holistically&lt;/em&gt;. When a requirement can't be met with real words, the model doesn't refuse. It fills the gap with plausible inventions.&lt;/p&gt;

&lt;p&gt;Removing the requirement fixed it. A re-test of &lt;em&gt;holistic&lt;/em&gt; produced the two real forms and nothing else. Different forms are still welcome when they appear naturally, but they are never forced.&lt;/p&gt;

&lt;p&gt;So, the next lesson: keep your eye on edge cases where the input makes the constraint impossible to follow. Ask what the model will do. Unfortunately, it usually sticks to the letter of the rule and pays for it in correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Reasonable gates, garbage output
&lt;/h2&gt;

&lt;p&gt;Back to &lt;code&gt;asdfgh&lt;/code&gt;. Each check in the pipeline was sensible on its own. Are all the words real? Yes, an apology is made of real words. Does the entered word appear? Yes, once, inside the apology. Are there enough sentences? Yes.&lt;/p&gt;

&lt;p&gt;What I missed was checking the relationship between each sentence and the target. Every sentence highlights the word it teaches. In the broken card, the highlighted words were &lt;em&gt;please&lt;/em&gt;, &lt;em&gt;tell&lt;/em&gt;, &lt;em&gt;choose&lt;/em&gt;: real words, just not the word the card was about.&lt;/p&gt;

&lt;p&gt;The fixes were specific. Each sentence's highlighted word must now match the entered word or a form of it, and anything else is rejected. Known refusal phrasings are detected and rejected too. And the exact sentences from that card became a regression test, so this failure can't quietly come back.&lt;/p&gt;

&lt;p&gt;The lesson I took here: a list of property checks is not a specification. Each gate checked that something looked normal. None checked that the output did its job.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. When the check measures a proxy
&lt;/h2&gt;

&lt;p&gt;In September, I noticed a card for &lt;em&gt;triage&lt;/em&gt; in production with ten medical sentences: the ER, a field clinic, a disaster drill, a triage nurse. Every sentence was correct. But &lt;em&gt;triage&lt;/em&gt; has a second sense that an office worker probably meets more often: prioritizing anything scarce, like a bug backlog, an inbox, or a budget. That sense was missing entirely.&lt;/p&gt;

&lt;p&gt;Ten sentences in one sense defeat the point of the card. They teach one corner of the word ten times.&lt;/p&gt;

&lt;p&gt;The worse news was in my review checklist. It checked variety by counting distinct context labels, and the &lt;em&gt;triage&lt;/em&gt; card had ten different labels: "ER", "disaster response", "drill" and so on. By my own checklist, the card passed. The checklist measured a proxy, labels, instead of the thing itself: meanings.&lt;/p&gt;

&lt;p&gt;The cause turned out to be a model setting. To save cost during bulk generation, the model ran with low reasoning effort, and at that level it follows its first association and stays there. The original card had zero non-medical sentences out of ten, and a regeneration had two. After raising the setting one step, the next generation gave five: IT support, a newsroom, email, a budget, project planning.&lt;/p&gt;

&lt;p&gt;Two lessons. When a check uses a proxy, test it against a known bad case before trusting it. And a setting chosen for cost is a quality decision, whether or not anyone treats it as one.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Strict gates fail silently
&lt;/h2&gt;

&lt;p&gt;The dictionary check is the strongest gate I have, and it has a cost I only found by looking for it.&lt;/p&gt;

&lt;p&gt;During Spanish testing, the dictionary rejected four real words as inventions: &lt;em&gt;afrutadas&lt;/em&gt; (fruity), &lt;em&gt;carísimos&lt;/em&gt; (very expensive), &lt;em&gt;desglasar&lt;/em&gt; (to deglaze) and &lt;em&gt;risotto&lt;/em&gt;. Spanish builds forms like &lt;em&gt;carísimo&lt;/em&gt; freely, and dictionaries don't list them all. Loanwords and specialist terms are often missing too.&lt;/p&gt;

&lt;p&gt;Nobody noticed, because nothing visibly broke. The rejected sentences were regenerated, the card still had ten sentences, and it shipped. But the replacements were plainer. A strict gate quietly strips out exactly the rich, natural vocabulary an advanced learner wants.&lt;/p&gt;

&lt;p&gt;English has the same problem. Our English dictionary doesn't know &lt;em&gt;triaging&lt;/em&gt;, a perfectly regular form. And the check that catches refusal text once threw out "I cannot condone lying to my friends", a natural sentence for &lt;em&gt;condone&lt;/em&gt;, because it sounded like the model saying no. In both cases the sentence was quietly replaced, and the card looked fine.&lt;/p&gt;

&lt;p&gt;A gate's misses show up as bugs. Its false alarms show up as nothing at all.&lt;/p&gt;

&lt;p&gt;My lesson here: watch what the validator removes, not only what it lets through.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Every new language reopens old failures
&lt;/h2&gt;

&lt;p&gt;Spanish needed its own dictionaries and its own rules. Accents, for example, are part of the word: &lt;em&gt;sí&lt;/em&gt; and &lt;em&gt;si&lt;/em&gt;, &lt;em&gt;tú&lt;/em&gt; and &lt;em&gt;tu&lt;/em&gt;, &lt;em&gt;papá&lt;/em&gt; and &lt;em&gt;papa&lt;/em&gt; are different words. A human review of generated Spanish cards then found failure types that English testing never showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Another language leaking in.&lt;/strong&gt; Italian &lt;em&gt;congiuntivo&lt;/em&gt; instead of Spanish &lt;em&gt;subjuntivo&lt;/em&gt; in a grammar field, and Chinese characters inside a Spanish context label.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The model's doubt written into the output.&lt;/strong&gt; A part-of-speech field that read "verbo? (incorrecto)".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One generic meaning copied onto all ten sentences&lt;/strong&gt;, instead of the meaning the word has in each sentence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A neighboring word taught instead of the target.&lt;/strong&gt; &lt;em&gt;restar&lt;/em&gt; (to subtract) for &lt;em&gt;resto&lt;/em&gt; (remainder).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gender that changes the meaning.&lt;/strong&gt; &lt;em&gt;La radio&lt;/em&gt; is broadcasting; &lt;em&gt;el radio&lt;/em&gt; is a radius. A wrong article silently changes the word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of these get past the dictionary, because it checks the sentence, not the metadata fields around it. Inside the sentences, the words involved are real.&lt;/p&gt;

&lt;p&gt;Italian and German come next. German uses gender to separate meanings even more often than Spanish does. I treat the list of past failures as the first draft of the test plan for each new language.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. When the prompt and the validator disagree
&lt;/h2&gt;

&lt;p&gt;In October, during a large import of new words for the app's word library, more than half of the cards came out short: six, seven, eight, or nine sentences instead of ten. There were no errors and no timeouts. I noticed it in the admin word list, where sixes and sevens kept showing up. The logs gave the scale: of 698 words generated in twelve hours, only 326 had all ten.&lt;/p&gt;

&lt;p&gt;The cause was two rules that disagreed. The prompt allowed close relatives of the word: &lt;em&gt;decide&lt;/em&gt;, &lt;em&gt;decision&lt;/em&gt;, &lt;em&gt;decisive&lt;/em&gt;. The validator, written after &lt;code&gt;asdfgh&lt;/code&gt;, accepts only grammatical forms of the same word, so it rejected &lt;em&gt;abhorrence&lt;/em&gt; on a card for &lt;em&gt;abhorrent&lt;/em&gt;. The target-word check caused 907 of 1,045 rejections, and most of them were derived words the prompt had invited. Both rules were reasonable, and both sat in the same methodology document, a few lines apart. Nobody noticed that they contradicted each other.&lt;/p&gt;

&lt;p&gt;Rejections alone would not have produced short cards, because a retry loop refills rejected slots. But in the batch import, one setting meant two things: the minimum number of sentences a card needs to ship, and the point where generation stopped trying. Once six sentences passed, the loop stopped. It took both bugs together to ship short cards, and every one of them passed as finished.&lt;/p&gt;

&lt;p&gt;We chose one rule: a card shows the entered word and its grammatical forms, never derived words, which belong on their own cards. Generation now keeps going until there are ten sentences or the retry limit is reached. Replaying the old rejections through the new validator showed that 93 of them had been real forms of the word all along, lost to a gap in the tool that maps a word to its dictionary form. We paused the import, fixed the rules, and regenerated 833 cards before any images were made, so no image money was spent twice. Of the 2,599 imported words, 2,594 now have ten sentences.&lt;/p&gt;

&lt;p&gt;The lesson: the generator's instructions and the checker's rules are one specification, so test them against each other, starting with every example in the prompt. Running &lt;em&gt;decision&lt;/em&gt; for &lt;em&gt;decide&lt;/em&gt; through the validator would have failed in a second. And watch the distribution of your output, not only pass and fail. A column full of sixes and sevens told me more than any single check.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the checks still miss
&lt;/h2&gt;

&lt;p&gt;Now the honest part. The lessons above apply to almost any LLM product. What follows doesn't. These are gaps specific to a vocabulary app, and that is exactly why they are still open: problems rooted in the domain don't give way to general rules. I list them less as lessons than as a sample of how varied quality problems get, even inside one narrow product:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sense variety has no automated check.&lt;/strong&gt; It depends on the prompt and the model setting. The &lt;em&gt;triage&lt;/em&gt; fix made it better, but it's not guaranteed. This is the biggest open gap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sentences about the word instead of with it.&lt;/strong&gt; For example, instead of using the word &lt;em&gt;bass&lt;/em&gt;, the model defined it: "The term bass often refers to the lowest range in a musical ensemble." Every word is real, and the target is present, so the gates pass it. Only the prompt discourages it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Words outside the word family.&lt;/strong&gt; &lt;em&gt;Mousetrap&lt;/em&gt; is highlighted as a form of &lt;em&gt;mice&lt;/em&gt;. It's a real word, so the dictionary accepts it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grammar.&lt;/strong&gt; A dictionary checks words, not how they combine. The same grammar mistake repeated across a card gets through.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive words.&lt;/strong&gt; The classifier that flags vulgar or offensive words under-flags in practice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images that contradict the sentence&lt;/strong&gt;, such as a person of a different gender than the sentence describes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are new to evaluating non-deterministic LLM output, expect a list like this in your own domain. It will be different, and it won't be shorter.&lt;/p&gt;

&lt;p&gt;Some of these problems can get a cheap mechanical check: a question mark in a grammar field is easy to catch. Others need a semantic judgment, and that is where an LLM judge may finally earn a place. Not to decide whether a word exists, but whether ten sentences really cover different meanings.&lt;/p&gt;

&lt;p&gt;As long as these gaps remain, human review covers what the gates don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I keep it honest
&lt;/h2&gt;

&lt;p&gt;Three habits hold this together. None of them is clever. They just stop knowledge from leaking away between one fix and the next.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;A failure gallery.&lt;/strong&gt; Every real failure goes into one file: the actual output (verbatim, where I still have it), why it passed the checks, what caused it, and the rule it produced. Real cases only, no hypotheticals. Entries are never deleted when fixed, only marked fixed, because fixed failures come back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One methodology document with a decision log.&lt;/strong&gt; What makes a good card is written down in one place, with the date and the reason for every rule. Any change to the generation prompt or a validator must update that document in the same pull request. On its own, it didn't prevent the contradiction in section 7; that takes tests, not just a document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression tests built from real output.&lt;/strong&gt; The &lt;code&gt;asdfgh&lt;/code&gt; sentences are a test fixture. A real failure makes a better test than one I would invent, because I would never have invented it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Much of the day-to-day review is done with AI assistants, which makes the written rules more important, not less. An assistant reviewing cards follows the same checklist a human would, and the &lt;em&gt;triage&lt;/em&gt; story shows what happens when the checklist is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you're building something similar
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't let the model grade its own facts.&lt;/strong&gt; Bring ground truth from outside: dictionaries, schemas, a database, anything the model didn't write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read every prompt constraint as a test case.&lt;/strong&gt; What does the model do when the input makes the constraint impossible?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check that the output does its job&lt;/strong&gt;, not only that it looks normal.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test each check against a known bad case&lt;/strong&gt;, especially when it measures a proxy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Watch what your gates remove.&lt;/strong&gt; False alarms cost quality that nobody sees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep real failures, verbatim.&lt;/strong&gt; They are the best test plan you will have for the next model, the next prompt or the next language.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test the generator's instructions against the checker's rules.&lt;/strong&gt; Start with every example in the prompt; one document is not enough.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Look at the distribution of your output&lt;/strong&gt;, not only at pass and fail. A histogram of results can show a bug that no single check reports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;LAFwords is live at &lt;a href="https://lafwords.com/?utm_source=devto" rel="noopener noreferrer"&gt;lafwords.com&lt;/a&gt; for learners of English and Spanish. If you work on LLM quality and see a gap in this approach, I'd like to hear about it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://lafwords.com/blog/testing-ai-generated-content/" rel="noopener noreferrer"&gt;LAFwords blog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>llm</category>
      <category>qa</category>
    </item>
    <item>
      <title>Using OTP sent via email in E2E UI tests.</title>
      <dc:creator>Andrey Vishin</dc:creator>
      <pubDate>Tue, 24 Jan 2023 06:09:41 +0000</pubDate>
      <link>https://dev.to/andreyvishin/using-otp-sent-via-email-in-e2e-ui-tests-4f5n</link>
      <guid>https://dev.to/andreyvishin/using-otp-sent-via-email-in-e2e-ui-tests-4f5n</guid>
      <description>&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;When implementing end-to-end automated testing of an application with one-time password  (OTP) authentication, we need to establish a way to test the entire flow of OTP delivery to the user.&lt;br&gt;
Here I provide two classes that are ready to be used in this kind of test to receive OTP via email sent by the backend and a step-by-step guide on integrating into your framework.&lt;/p&gt;
&lt;h2&gt;
  
  
  Constraints
&lt;/h2&gt;

&lt;p&gt;To implement this approach, the following conditions must be met:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The QA team manages a dedicated email account.&lt;/li&gt;
&lt;li&gt;Emails delivering OTP have a known constant Subject text&lt;/li&gt;
&lt;li&gt;A known constant phrase precedes OTP in the body of the email&lt;/li&gt;
&lt;li&gt;The OTP length is fixed&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Algorithm
&lt;/h2&gt;

&lt;p&gt;Along execution, an autotest: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates and keeps an instance of the Email Provider service based on the configuration settings (Email Provider API credentials)&lt;/li&gt;
&lt;li&gt;Gets and keeps a pointer to the last received email with a subject that is set for OTP emails (or retains null if no one was received before)&lt;/li&gt;
&lt;li&gt;Triggers the OTP generation and delivery&lt;/li&gt;
&lt;li&gt;Waits for email delivery to the Email Provider&lt;/li&gt;
&lt;li&gt;Gets the email message&lt;/li&gt;
&lt;li&gt;Parses OTP from the message&lt;/li&gt;
&lt;li&gt;Uses OTP to verify login to the application under test&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 1-2 and 4-6 are implemented in the code provided.&lt;/p&gt;
&lt;h2&gt;
  
  
  EmailProviderHandler interface
&lt;/h2&gt;

&lt;p&gt;To make the approach flexible, the &lt;code&gt;EmailProviderHandler&lt;/code&gt; interface has been declared. The implementation must provide the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a service initiation to expect emails with specified subject&lt;/li&gt;
&lt;li&gt;checking if a new email with the subject has already been received&lt;/li&gt;
&lt;li&gt;getting the message from the received email&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;em&gt;Interface&lt;/em&gt;
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EmailProviderHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isEmailReceived&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;p&gt;I have implemented the interface for Gmail (see below), but you can implement it for another provider you use.&lt;/p&gt;

&lt;h2&gt;
  
  
  EmailedOTPHandler class
&lt;/h2&gt;

&lt;p&gt;To process emails with OTP, use the &lt;code&gt;EmailedOTPHandler&lt;/code&gt; class.&lt;br&gt;
An EmailedOTPHandler instance should be created for a specific combination of an email Subject, a passphrase followed by an OTP in the email body, and an OTP length. To give the handler a tool to access emails on a particular Email Provider, we should inject an instance of the implemented &lt;code&gt;EmailProviderHandler&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  &lt;em&gt;Class variables list&lt;/em&gt;
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Subject pattern of the emails containing the OTP&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;otpKeyPhrase&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Phrase that precedes the OTP in the email body&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Length of the OTP&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;EmailProviderHandler&lt;/span&gt; &lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Gmail service&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;p&gt;Before using an &lt;code&gt;EmailedOTPHandler&lt;/code&gt; instance for getting OTP, you need to initiate it using the &lt;code&gt;init()&lt;/code&gt; method. &lt;br&gt;
Then you can trigger OTP generation and delivery by either mimic user login behavior through your application's UI or by querying the BE endpoint.&lt;br&gt;
To get the OTP from an email, use the &lt;code&gt;getOTPEmailSent()&lt;/code&gt; method. The method waits for a new email with Subject set and then tries to parse the OTP from it.&lt;br&gt;
If there is no new message within the time period, NULL is returned.&lt;/p&gt;

&lt;p&gt;The complete class code is shown below.&lt;/p&gt;
&lt;h2&gt;
  
  
  GmailHandler class
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GmailHandler&lt;/code&gt; implements &lt;code&gt;EmailProviderHandler&lt;/code&gt; to handle a Gmail service through API. &lt;br&gt;
The methods described in the &lt;a href="https://developers.google.com/gmail/api/quickstart" rel="noopener noreferrer"&gt;Google Gmail Java Quick Start&lt;/a&gt; guide are used to start the Gmail service and get credentials.&lt;/p&gt;

&lt;p&gt;On the first Gmail API call, &lt;code&gt;GmailHandler&lt;/code&gt; creates a credential file in the project to authenticate all future calls to the Gmail service (see detailed description below).&lt;/p&gt;

&lt;p&gt;The complete class code is shown below.&lt;/p&gt;
&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/vishin-andrey/OTP-handler" rel="noopener noreferrer"&gt;Java&lt;/a&gt; (used in this article)&lt;br&gt;
&lt;a href="https://github.com/vishin-andrey/otp-handler-python" rel="noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Notice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;To enable Java &lt;code&gt;assert&lt;/code&gt; validation,  use the JVM parameter &lt;code&gt;-ea&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;/p&gt;
  IntelliJ IDEA
  &lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh7gvcftxk9z95k33neh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzh7gvcftxk9z95k33neh.png" alt="IntelliJ IDEA interface for setting the JVM parameters" width="800" height="307"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Before using the class, you must enable and configure the API for your Gmail account, as shown below.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;GmailHandler&lt;/code&gt; extracts OTP from the email snippet. If your OTP in the email body is too far from the beginning and therefore not included in the snippet, use &lt;code&gt;getPayload()&lt;/code&gt; instead of &lt;code&gt;getSnippet()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to set up the Gmail account API
&lt;/h2&gt;

&lt;p&gt;Before moving on, you must activate and configure the API for the Gmail account you will be using to receive OTP emails. Using the &lt;a href="https://console.cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt; follow the steps below.&lt;/p&gt;

&lt;h4&gt;
  
  
  Register a new project
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;ul&gt;
&lt;li&gt;Click on CREATE PROJECT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuic3ksh7bfqcxyoucbf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcuic3ksh7bfqcxyoucbf.png" alt="Adding a new project in the Google Cloud Console" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Then give your project a name.&lt;/li&gt;
&lt;/ul&gt;

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



&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Enable API
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;ul&gt;
&lt;li&gt;Click on ENABLE APIS AND SERVICES.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Search for Gmail in the API Library.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Enable Gmail API&lt;/li&gt;
&lt;/ul&gt;

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



&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Create credentials for autotests to access your Gmail account
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;ul&gt;
&lt;li&gt;Click on CREATE CREDENTIALS.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Choose for Gmail API a &lt;code&gt;User data&lt;/code&gt; type.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Customize the OAuth Consent Screen - enter any name for the app and add your contact email address&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Set the scope
It make sense to choose the &lt;code&gt;read only&lt;/code&gt; scope&lt;/li&gt;
&lt;/ul&gt;

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

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

&lt;ul&gt;
&lt;li&gt;Choose the &lt;code&gt;Desktop app&lt;/code&gt; application type and give it a name&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Your credentials have been created; you need to download the Client ID file in JSON format.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;You can also customize your credentials at any time in the Credentials tab and then download the updated JSON file.&lt;/li&gt;
&lt;/ul&gt;

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

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



&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Register a trusted test user
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;ul&gt;
&lt;li&gt;Navigate to the OAuth consent screen tab and click on &lt;code&gt;ADD USERS&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Add your any real Gmail account email address. You will need to act under this account later to verify access for the Client ID&lt;/li&gt;
&lt;/ul&gt;

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



&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to add the Gmail account credentials to the project
&lt;/h2&gt;

&lt;p&gt;After receiving the Client ID file in JSON format (as shown above), you must exchange it for the &lt;code&gt;StoredCredential&lt;/code&gt; file the first time you call the Gmail API.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add JSON Client ID file
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;ul&gt;
&lt;li&gt;Put the file into &lt;code&gt;src/main/resources/credentials&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft47rol8ailyqaqbpozg9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft47rol8ailyqaqbpozg9.png" alt="JSON Client ID file in the project structure" width="489" height="457"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Verify the autotest access to the account on Gmail
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;br&gt;
Run your project first time. At the first call to the Gmail API, a browser will be opened by Google. Your should follow the Google dialog.

&lt;ul&gt;
&lt;li&gt;Choose the account you add on the Register a trusted test user step.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuj8qaoq1etfkmopooi09.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuj8qaoq1etfkmopooi09.png" alt="Access verification - choose account alert" width="687" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Continue&lt;/code&gt; to verify the app&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Click on &lt;code&gt;Continue&lt;/code&gt; to grant the access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdeayf0bgpytwxb6sve98.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdeayf0bgpytwxb6sve98.png" alt="Access verification - granting access scr" width="692" height="1006"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check for the confirmation&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Stop the first test project execution.&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Check for the StoredCredential file
&lt;/h4&gt;

&lt;p&gt;&lt;/p&gt;
  Step by step
  &lt;ul&gt;
&lt;li&gt;The &lt;code&gt;StoredCredential&lt;/code&gt; file should already be created automatically in &lt;code&gt;src/main/resources/credentials&lt;/code&gt; during your first Gmail API call; if it is not, repeat this section again.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fae2bbrwrzbvvqcf80mdk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fae2bbrwrzbvvqcf80mdk.png" alt="StoredCredential file in the project structure" width="493" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you change the Gmail API configuration in the console in the future, you should delete the &lt;code&gt;StoredCredential&lt;/code&gt; file and repeat these steps to add a new one.&lt;/p&gt;



&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Full code
&lt;/h2&gt;

&lt;h3&gt;
  
  
  EmailedOTPHandler
&lt;/h3&gt;

&lt;p&gt;&lt;/p&gt;
  Code
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;gmail&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;EmailProviderHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isEmailReceived&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EmailedOTPHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Subject pattern of the emails containing the OTP&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;otpKeyPhrase&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Phrase that precedes the OTP in the email body&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Length of the OTP&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;EmailProviderHandler&lt;/span&gt; &lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Gmail service&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;EmailedOTPHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;otpKeyPhrase&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmailProviderHandler&lt;/span&gt; &lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emailSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;otpKeyPhrase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;otpKeyPhrase&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;otpLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emailProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Parse and return OTP from the email with id = this.emailID
     */&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;parseOTP&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;mailText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// Parse OTP&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mailText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;indexOf&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;otpKeyPhrase&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// Find the OTP key phrase&lt;/span&gt;
        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"OTP key phrase not found in the email"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;otpKeyPhrase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;         &lt;span class="c1"&gt;// Move to the OTP start position&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mailText&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;substring&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Trying to get a new email, checking for a new message every 5 sec for 6 times.
     * If gotten a new message, return the OTP from it.
     * If there is no new message during the time period, return NULL
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getOTP&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;attempt&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmailReceived&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parseOTP&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GmailHandler
&lt;/h3&gt;

&lt;p&gt;&lt;/p&gt;
  Code
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;gmail&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.auth.oauth2.Credential&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.googleapis.javanet.GoogleNetHttpTransport&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.http.javanet.NetHttpTransport&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.json.JsonFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.json.jackson2.JacksonFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.client.util.store.FileDataStoreFactory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.services.gmail.Gmail&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.services.gmail.GmailScopes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.services.gmail.model.ListMessagesResponse&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.google.api.services.gmail.model.Message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.nio.file.Files&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.security.GeneralSecurityException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.*&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GmailHandler&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;EmailProviderHandler&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;GMAIL_AUTHENTICATED_USER&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"me"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;JsonFactory&lt;/span&gt; &lt;span class="no"&gt;JSON_FACTORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;JacksonFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDefaultInstance&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;APPLICATION_NAME&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Gmail handler"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;PATH_TOKEN_DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getProperty&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user.dir"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/src/main/resources/credentials"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="no"&gt;PATH_CREDENTIALS_FILE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PATH_TOKEN_DIRECTORY&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/gmail_credentials.json"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Gmail&lt;/span&gt; &lt;span class="n"&gt;gmailService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;SCOPES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GmailScopes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MAIL_GOOGLE_COM&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ID of the last email with the provided Subject&lt;/span&gt;


    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;emailSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;startService&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;emailID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getEmailID&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Start a new Gmail service
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;startService&lt;/span&gt; &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NetHttpTransport&lt;/span&gt; &lt;span class="no"&gt;HTTP_TRANSPORT&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="no"&gt;HTTP_TRANSPORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GoogleNetHttpTransport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newTrustedTransport&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;gmailService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Gmail&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;HTTP_TRANSPORT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;JSON_FACTORY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getCredentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;HTTP_TRANSPORT&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setApplicationName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;APPLICATION_NAME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;GeneralSecurityException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Creates an authorized Credential object.
     * This is the code from the Google Developer Console documentation.
     *
     * @param HTTP_TRANSPORT The network HTTP Transport.
     * @return An authorized Credential object.
     * @throws IOException If the credentials.json file cannot be found.
     */&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Credential&lt;/span&gt; &lt;span class="nf"&gt;getCredentials&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;NetHttpTransport&lt;/span&gt; &lt;span class="no"&gt;HTTP_TRANSPORT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;InputStream&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Files&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newInputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PATH_CREDENTIALS_FILE&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;toPath&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;FileNotFoundException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Resource not found: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="no"&gt;PATH_CREDENTIALS_FILE&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="nc"&gt;GoogleClientSecrets&lt;/span&gt; &lt;span class="n"&gt;clientSecrets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GoogleClientSecrets&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;JSON_FACTORY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;InputStreamReader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

        &lt;span class="c1"&gt;// Build flow and trigger user authorization request.&lt;/span&gt;
        &lt;span class="nc"&gt;GoogleAuthorizationCodeFlow&lt;/span&gt; &lt;span class="n"&gt;flow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GoogleAuthorizationCodeFlow&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="no"&gt;HTTP_TRANSPORT&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;JSON_FACTORY&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clientSecrets&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;SCOPES&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDataStoreFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FileDataStoreFactory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;PATH_TOKEN_DIRECTORY&lt;/span&gt;&lt;span class="o"&gt;)))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setAccessType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"offline"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;LocalServerReceiver&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LocalServerReceiver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Builder&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setPort&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8888&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AuthorizationCodeInstalledApp&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flow&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;receiver&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;authorize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Return ID of the last email with subject = emailSubject
     * @return - last email id
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getEmailID&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;listOfMessages&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;ListMessagesResponse&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gmailService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;list&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;GMAIL_AUTHENTICATED_USER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setQ&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"subject:"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;emailSubject&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;listOfMessages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessages&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listOfMessages&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;listOfMessages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;listOfMessages&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Return the email snippet text
     * @return - email snippet text as a String
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Message&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gmailService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;users&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;GMAIL_AUTHENTICATED_USER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;emailID&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;execute&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getSnippet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Use getPayload() instead if you want to get the full email body&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/**
     * Check if there is a new email with subject = emailSubject
     * @return
     */&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isEmailReceived&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;newEmailID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getEmailID&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newEmailID&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newEmailID&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailID&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;emailID&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newEmailID&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test example
&lt;/h3&gt;

&lt;p&gt;&lt;/p&gt;
  Code
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gmail.EmailedOTPHandler&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gmail.GmailHandler&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

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

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"OTP test"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;keyPhrase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Your OTP is: "&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="nc"&gt;GmailHandler&lt;/span&gt; &lt;span class="n"&gt;gmail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GmailHandler&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;EmailedOTPHandler&lt;/span&gt; &lt;span class="n"&gt;otpHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EmailedOTPHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;keyPhrase&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;otpLength&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;gmail&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;otpHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="c1"&gt;// -&amp;gt; Here trigger the OTP email sending&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;otpHandler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOTP&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"No new email with subject = '"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"' was received during the time period"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"OTP: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;otp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>java</category>
      <category>tutorial</category>
      <category>python</category>
    </item>
  </channel>
</rss>
