はじめに
本記事ではこちらでHarold_Owenさんが公開してくれているコードを元にカスタムキーワードを作成し、Google提供のGmaiAPIを使ってGmailのメールを取得・本文をログに表示するデモです。なるべく細かく書こうと思っていますが、わからない部分があればお知らせもらえると助かります。
Google Workspaceでの作業
Google Workspace APIのJava向けのクイックスタートを確認しながらデスクトップ アプリケーションの認証情報を承認するところまで進めます。
新しいプロジェクトを作成
Google Cloud プロジェクトをまだ持っていない場合は作成します
https://console.cloud.google.com/projectcreate
プロジェクト名はなんでもいいです。
API有効化
クイックスタートの案内から「APIの有効化」をします。
認証情報の作成(同意画面の設定)
Google Cloud コンソールで、メニュー アイコン > [API とサービス] > [認証情報] の順に選択します。
[認証情報を作成] > [OAuth クライアント ID] をクリックします。
同意画面の設定へ遷移します
今回使っているGoogleアカウントは組織はなく個人利用なので「外部」を選択しています。
使いたいほうを選択して「作成」します。
アプリ情報を入力します
任意のアプリ名、サポートメールアドレス、デベロッパーの連絡先メールアドレスを設定します。
GmailAPIで絞込をかけて[.../auth/gmail.readonly]を追加します
テストユーザーの設定をします。「ADD USERS」から使用するメールアドレスを追加します。
保存して次へ進み、入力情報を見直して「ダッシュボードに戻る」で完了します。
認証情報の作成(同意画面の設定後)
[認証情報を作成] > [OAuth クライアント ID] をクリックします。
[アプリケーションの種類] > [デスクトップ アプリ] をクリックします。
[名前] フィールドに、認証情報の名前を入力します。この名前は Google Cloud コンソールにのみ表示されます。
[作成] をクリックします。
OAuth クライアントの作成画面が表示され、新しいクライアント ID とクライアント シークレットが表示されます。
[JSONをダウンロード]してから[OK] をクリックします。
新しく作成された認証情報が [OAuth 2.0 Client ID] に表示されます。
ダウンロードした JSON ファイルを [credentials.json]として保存します。
Katalonのプロジェクトにある[Data Files]ディレクトリに[gmail]ディレクトリを作成し[credentials.json]を配置します。
以上でGoogle Workspaceでの作業は終わりです
Katalon Studioでの作業
Custom keyword(カスタムキーワード)の作成
Gmailの受信処理を各テストケースで使いますためにキーワードを作成します。
プロジェクトの[Keywords]から任意パッケージを右クリックし[new]>[keyword]で作成します。
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.gson.GsonFactory;
import com.google.common.io.BaseEncoding
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.gmail.Gmail;
import com.google.api.services.gmail.GmailScopes;
import com.google.api.services.gmail.model.ListMessagesResponse;
import com.google.api.services.gmail.model.Message;
import com.google.api.services.gmail.model.MessagePartHeader
import com.kms.katalon.core.annotation.Keyword
import com.kms.katalon.core.configuration.RunConfiguration
import com.kms.katalon.core.util.KeywordUtil
import com.kms.katalon.core.webui.keyword.WebUiBuiltInKeywords as WebUI
public class GmailUtil {
private static final String APPLICATION_NAME = "gmail-api-sample";
private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
private static final int MAX_WAIT_SEC = 60;
private static final String USER = "me";
/**
* このクイックスタートで必要なスコープのGlobal instance
* これらのスコープを変更する場合は、以前に保存したtokens/ folderを削除してください。
*/
private static final List<String> SCOPES = Collections.singletonList(GmailScopes.MAIL_GOOGLE_COM);
/**
* 指定件名にヒットするメールの本文を取得する
* @param subjectPrefix メール件名
* @return message メール本文
*/
@Keyword
public String getEmailLink(String subjectPrefix) {
Gmail service = logonToGmail();
Message message = getFirstRelevantMessage(service, subjectPrefix);
return parseMessage(message);
}
private Gmail logonToGmail() {
// Build a new authorized API client service.
final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
return new Gmail.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT))
.setApplicationName(APPLICATION_NAME)
.build();
}
private Message getFirstRelevantMessage(Gmail service, String subjectPrefix) {
int waitSec = 10;
int totalWaitSec = 0;
while(totalWaitSec < MAX_WAIT_SEC) {
ListMessagesResponse listResponse = service.users().messages().list(USER).execute();
List<Message> messages = listResponse.getMessages();
if (messages) {
for(Message message in messages) {
message = service.users().messages().get(USER, message.getId()).execute();
if(isRelevant(service, message, subjectPrefix)) {
return message;
}
}
}
println String.format('No relevant Gmail found, checking again after %d seconds...', waitSec)
WebUI.delay(waitSec);
totalWaitSec += waitSec;
}
KeywordUtil.markErrorAndStop(String.format('No Gmail found after %d seconds with subject %s', totalWaitSec, subjectPrefix))
}
private boolean isRelevant(Gmail service, Message message, String subjectPrefix) {
List<MessagePartHeader> headers = message.getPayload().getHeaders()
for(MessagePartHeader header in headers) {
if('Subject'.equals(header.getName())) {
String subject = header.getValue();
if(subject && subject.startsWith(subjectPrefix)) {
return true;
}
}
}
return false;
}
/**
* 取得したJSONデータから本文を抜き取る
*/
private String parseMessage(Message message) {
System.out.println(message.toPrettyString());//デバッグ用
// bodyの取得
//String data = message.getPayload().getBody().getData();
String data = message.getPayload().getParts().get(0).getBody().get("data").toString();
if(data) {
return new String(BaseEncoding.base64Url().decode(data));
}
KeywordUtil.markErrorAndStop(String.format('Gmail no body data, message ID %s', message.getId()));
}
private Credential getCredentials(NetHttpTransport HTTP_TRANSPORT) {
'CAUTION: If you change user scopes, delete the previously saved tokens folder.'
String tokensPath = RunConfiguration.getProjectDir() + '/Data Files/gmail/tokens'
String credentialsPath = RunConfiguration.getProjectDir() + '/Data Files/gmail/credentials.json'
InputStream input = new FileInputStream(new File(credentialsPath))
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(input));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(new FileDataStoreFactory(new java.io.File(tokensPath)))
.setAccessType("offline")
.build();
LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build();
return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
}
}
追記(2023.06.05)
本文を取り出すString data =
のところですが、残念ながらメールの種類によって本文の階層が異なるため失敗することがあるようでした。デバッグ用のSystem.out.println(message.toPrettyString())
でデータの内容が見えるはずなので失敗した場合はデータに合わせてString data =
の処理を変更してください。
注意すべきは本文はエンコードされているため読める形式ではないことです。他の方がデータの例を合わせて説明してくれているコチラの記事が参考になります。
必要ライブラリの準備
このままだと上述で記載のコードはこのように依存関係のエラーになると思います。
依存関係を解決するためにgradleでライブラリを準備します。gradleの導入については別記事で説明していますため、そちらを参照ください。
build.gradleへ記述
[build.gradle]にてdependencies部分に以下を追加します
dependencies {
implementation 'com.google.api-client:google-api-client:2.0.0'
implementation 'com.google.oauth-client:google-oauth-client-java6:1.34.1'
implementation 'com.google.oauth-client:google-oauth-client-jetty:1.34.1'
implementation 'com.google.apis:google-api-services-gmail:v1-rev20230403-2.0.0'
}
katalonCopyDependenciesの実行
一度Katalonを閉じてからコマンドプロンプトでプロジェクトへ移動cd プロジェクトのパス
してからgradle katalonCopyDependencies
を実行します。
以下のようにBUILD SUCCESSFULが表示されればOKです。
C:\Users\user>cd C:\Users\user\Katalon Studio\sample20230420
C:\Users\user\Katalon Studio\sample20230420>gradle katalonCopyDependencies
Starting a Gradle Daemon (subsequent builds will be faster)
BUILD SUCCESSFUL in 30s
1 actionable task: 1 executed
再度Katalonを開き、Keywordsの「GmailUtil.groovy」を確認するとimport ~の部分のエラー表示が消えていると思います。
テストケースの作成
いよいよテストケースでカスタムキーワードを呼び出して使ってみます。
既存のテストケースへ記述してもいいですが、問題が起きた時に切り分けやすいように最初は新規テストケースの作成で試すことをお勧めします。
テストケースで必要な記述は以下の通りです。先に作成したCustomKeywordsを呼び出し、メールの件名を渡すことで本文を取得します。
String subject = 'ここにメールの件名をいれましょう'
String message = CustomKeywords.'GmailUtil.getEmailLink'(subject)
WebUI.comment(message)
今回は取得したメッセージをWebUI.comment()で出力していますが、テキストに書き出すなり用意した文言と比較するなり使い方はお任せします。
実行
実行すると初回はブラウザが開きアクセス許可が求められますので認証処理を行う必要があります。
ブラウザから許可したことで、プロジェクトのData Filesディレクトリにトークン情報(tokens)が保管されるため、二回目以降はブラウザからの許可は不要になります。
注意
Gitを使っている場合、作成した[ tokens ]ディレクトリ内は[ .gitignore ]に追加するなどして共有で使わないことをお勧めします。
tokensディレクトリ内をGitに入れない場合以下追加
Data Files/gmail/tokens/
gmailディレクトリ内からGitに入れない場合以下追加
Data Files/gmail/
実行完了すると以下のようにメール本文を確認できます
※NoSuchMethodErrorがある場合はコチラの記事を参照
お疲れさまでした
お疲れさまでした!!!