はじめに
こんにちは。前年エンジニアリング・トライアルについて記事を書きました@tsugumiriです。
今年は、前年に引き続き審査の業務に従事しつつ、エンジニアリング・トライアル2年生として、牛歩の歩みながら知見を深めて参りました。
今回は、取り組みを通じ制作した、Jira Cloud REST APIとGASを使ったチケット起票の仕組みについて、頭を悩ませた結果、エンジニアさんにアドバイスいただき、解決に至ったものがありましたので、紹介させていただければと思います。
Jira Software Cloudへの移行
今年経験した弊社の大きなイベントとして、Jira Software Cloudへの移行がありました。
Jira Software Cloudとは、チケット管理を基本とした、バグトラッキングや課題管理、プロジェクト管理に用いられるAtlassian社の製品です。
品質管理の業務に従事されている方にとっては、おなじみのツールではないかと思います。
審査業務においては、QA業務ほど多くのチケット起票は発生しないものの、できることへの工数削減として、移管とともに、Google Apps Script (以降GASと記載します)を利用した、チケット起票の自動化に取り組むことになりました。
Jira Cloud REST APIとGASを使ったチケット起票
チケット起票の自動化については、公式ドキュメントとともにQiita内の以下記事を参考とさせていただきました。
今回は、チケット起票に関する基本的な内容はすでに記事があることから、複数名で仕組みを利用することを前提とした上で、個人のアクセストークンをどのように管理するか、制作にあたり頭を悩ませた結果、エンジニアさんにアドバイスいただいた内容について、後述いたします。
個人のアクセストークンをどのように保管するか
今回、ツールを利用する際のAPI トークンは、最終的に、個人で発行可能なものを利用する方法としました。
この方法に行き着くまでには、共通アカウントの利用なども検討しましたが、審査業務は、社内の多くのプロジェクトに横断的に関わることから、共通アカウントを利用するよりは、セキュリティの側面からも、すでにJiraのプロジェクトに対し、必要な範囲の権限を所持している、個人のアカウントで投稿を行えたほうがよいのではないかと、アドバイスをいただきました。
また、個人のトークンを、ツールを利用する他者に閲覧されることなく、登録する仕組みについてアドバイスをいただいた方法が、Class PropertiesServiceのUserPropertyに登録できる仕組みとする点でした。
アクセストークンを登録・削除するためのコード
Jiraのアクセストークンを登録・削除する仕組みは、以下のようなコードで構築しました。
//以下をスプレッドシート起動時のトリガーとして設定することでUIにメニューを表示する
function createUi()
{
SpreadsheetApp.getUi()
.createMenu('メニュー')
.addItem('アクセストークンを登録する', 'setJiraToken')
.addItem('アクセストークンを削除する', 'deleteJiraToken')
.addToUi()
}
var userProperties = PropertiesService.getUserProperties();
var jira_user_key = getUserKey();
//UserKeyは任意の接頭語とメールアドレスで生成する
function getUserKey()
{
var jira_user_name = Session.getActiveUser().getEmail();
var jira_user_key = 'JIRA_TOKEN_' + jira_user_name;
return jira_user_key;
}
//JIRAトークンをUserPropertiesに登録する
function setJiraToken()
{
//すでにトークンが登録されているかのチェックを行う。
if (hasJiraToken(jira_user_key)) {
if (Browser.msgBox("すでに登録が完了しています。既存の登録内容を上書きしますか?", Browser.Buttons.OK_CANCEL) != 'ok') {
Browser.msgBox('登録を中止しました。終了します');
return
}else{
userProperties.deleteProperty(jira_user_key);
}
}
var input = Browser.inputBox('アクセストークンを入力してください\\n※アクセストークンは他の人へ共有しないでください', Browser.Buttons.OK_CANCEL)
if (input != '' || 'cancel') {
var token = input
userProperties.setProperty(jira_user_key, token);
Browser.msgBox('アクセストークンを登録しました。終了します');
return
}else{
Browser.msgBox('登録を中止しました。終了します');
}
}
//user_keyに紐づくトークンがすでに登録されているかを確認する
function hasJiraToken(user_key)
{
keys = userProperties.getKeys();
var ret = false
keys.forEach(function(col){
if (col == user_key) {
ret = true
}
})
return ret
}
//トークンの削除を行う仕組み
function deleteJiraToken()
{
if (hasJiraToken(jira_user_key)) {
if (Browser.msgBox("登録済みのアクセストークンを削除しますか?", Browser.Buttons.OK_CANCEL) != 'ok') {
Browser.msgBox('中止しました。終了します');
return
}
userProperties.deleteProperty(jira_user_key);
Browser.msgBox('削除を行いました。終了します');
return
}else{
Browser.msgBox('登録されているアクセストークンはありません。終了します');
}
}
登録が行えているかの確認については、以下のコードで行いました。
function showUserProperties()
{
Logger.log(userProperties.getProperties())
}
登録を行ったアカウントでの出力結果は以下となりました。
{JIRA_TOKEN_XXXXXX@XXXX.net=XXXXX}
登録を行ったアカウント以外での出力結果は以下となりました。
{}
確認の結果からも、個人のアクセストークンを、ツールを利用する他者に閲覧されることなく、登録することができていることが確認できました。
チケット起票を行うコード
上記で登録を行ったアクセストークンを利用したチケット起票の仕組みは、以下のようなコードで構築しました。
なお、今回、JSON部分は、仮のものとして記載しております。実際の制作時は、以下のドキュメントを参考とし、スプレッドシートから取得したデータをもとに、構築いたしました。
var scriptProperties = PropertiesService.getScriptProperties();
var userProperties = PropertiesService.getUserProperties();
function createJiraIssue()
{
//JSON部分は仮の内容として作成
var json = createJSON();
try{
//投稿結果(戻り値)は後続の結果をスプレッドシートに記録する動作などに用いる
var result = postJiraIssue(json)
}catch(e){
console.error('エラーが発生しました:' + e["message"]);
}
}
function createJSON()
{
var ret = {
"fields": {
"summary": 'summary',
"project": {
"key": 'project_name'
},
"description":'XXXXX',
"issuetype": {
"id": 'XXXXX'
}
}
}
return JSON.stringify(ret);
}
function postJiraIssue(json)
{
//呼び出し先のAPIのURLを設定。なおJIRA_BASE_URLはスクリプトプロパティに登録
var script_prop = scriptProperties.getProperties();
var request_url = script_prop["JIRA_BASE_URL"] + 'rest/api/2/issue/';
//optionsに含める情報構築のためメールアドレスを取得する
var jira_user_name = Session.getActiveUser().getEmail();
var jira_user_key = getUserKey();
var user_prop = userProperties.getProperties();
var token = Utilities.base64Encode(jira_user_name + ':' + user_prop[jira_user_key], Utilities.Charset.UTF_8);
var options = {
method: 'post',
payload: json,
contentType: 'application/json',
headers: {'Authorization': ' Basic ' + token},
muteHttpExceptions:true,
}
var response = UrlFetchApp.fetch(request_url, options);
var response_code = response.getResponseCode();
var response_body = response.getContentText();
if (response_code != 201) {
// 戻り値より取得したresponse_codeが201以外の場合はエラーの扱いとする
throw new Error(`code:${response_code} responseBody:${response_body}`);
}else{
return JSON.parse(response_body);
}
}
//UserKeyは任意の接頭語とメールアドレスで生成する
function getUserKey()
{
var jira_user_name = Session.getActiveUser().getEmail();
var jira_user_key = 'JIRA_TOKEN_' + jira_user_name;
return jira_user_key;
}
取り組みを通じ得られた知見
今回アドバイスをいただいたUserPropertyを利用する仕組みは、その性質上、同一シートを複数名で利用するシーンが多いGoogle スプレッドシートを利用し、他の仕組みへ投稿を行う際に、セキュリティに配慮した方法でサービスに対し、アクセスに必要な情報を管理できる方法でした。
情報の管理に対する知識や意識付けは、社内研修で学ぶ機会があります。また、エンジニアリング・トライアルでは、ツールの設計や、コードの記述方法をアドバイスいただくことが多いですが、今回は、加えて、機密情報の管理に対する知識とその方法について、学ぶことができました。
セキュリティと言う言葉を聞く機会は多いですが、いざ、自分がツールを作成しているときに、特に初学者の自分が、どれだけセキュリティに配慮した設計とできているかについては、自己評価が難しいと感じます。今回は、制作の初期の段階で、アクセストークンの管理方法のアドバイスをいただけたことで、その重要さについて、改めて認識しました。
さいごに
GASは身近なツールであり、Google スプレッドシートと組み合わせることで、多くの自動化が実現できます。
今年もエンジニアリング・トライアルを通じ、いくつかのツール制作に取り組ませていただくことで、できる「自動化」の方法をより増やすことができました。
牛の歩みも千里を心に、来年も知見を深めたいと考えています。
最後までお読みいただきありがとうございました。