JIRA APIでissueの変更履歴を取得する
の発展形で、実行を自動化したときに書いたコードです。
やりたいこと
JIRAのissue keyをA列に取得しておいて
それらのステータスの最終変更日時を取得してシートに書き出したい。
ということをやるときに以下の点も考慮しました。
- 対象のissue keyが多いとGASの6分の実行制限にかかるので
- 1課題1秒くらいとして1分間に余裕持って50課題の履歴を取得する
- 1分ごとのトリガーを用意して
- ヘッダにstatus_last_changed, from, toの列を自動追加して
- 次回実行はstatus_last_changedの列で値が入力されている次の行から
- 前回の実行が終わっていなければもう1分待つ
- 最後の実行時に残りが50課題も無ければあるだけ実行して
- トリガーを自動で削除する
コードは長くなりましたが、以下の4つの変数を設定すれば動くように書きました。
- ISSUE_URL
- ss_url
- rownums
- issue_key_col
事前準備
- JIRAのissue keyをA列に書き出しておく
- スクリプトのプロパティにtokenという名でbasic認証用の文字列を入れておく
関連記事
JIRA APIでissueの変更履歴を取得する
JIRA APIでissueの変更履歴を取得する2
JIRA APIでissueの変更履歴を取得する3
コード.gs
var ISSUE_URL = 'https://SITENAME.atlassian.net/rest/api/2/issue/'; var ss_url = "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit#gid=0"; var rownums = 50; var issue_key_col = 1;// A列にissue keyがある想定で1列目 /************************************ 初回実行でトリガーを登録する ************************************/ function initialize() { create_header_values(); console.log('トリガーを登録します' + new Date()); set_property('running', false); createTriggerEvery1Minutes(); } function create_header_values() { var sheet = get_sheet(); var valeus = [ "status_last_changed", "from", "to" ]; set_header(sheet, valeus); } /************************************ トリガーを作成する ************************************/ function createTriggerEvery1Minutes() { ScriptApp.newTrigger('run') .timeBased() .everyMinutes(1) .create(); } /************************************ run 1分ごとに実行される ************************************/ function run() { if(getProps('running') === 'true') { console.log('前の処理が終わっていないので次の1分を待つ' + new Date()) } else { console.log('前の処理が終わっているので実行する' + new Date()) set_property('running', true); get_last_status_change_date(); } } /************************************ runで判定後に実行される シートを取得して シートからJIRAのissue keyを取得して 最終ステータスの日時と何から何へを取得して シートに書き込む ************************************/ function get_last_status_change_date() { var sheet = get_sheet(); var keys = get_keys(sheet);// ["KT-6", "KT-7"];// 列で値が入っている最終行の下から50行分取得して1分おきにトリガーセットして完了したらトリガー削除 var last_statuses = get_last_statuses(keys); var values = create_value(last_statuses, sheet); set_values(values, sheet); set_property('running', false); } /************************************ 指定したss_urlのシートの0番目のシートを取得して返す ************************************/ function get_sheet() { var ss = SpreadsheetApp.openByUrl(ss_url); var sheet = ss.getSheets()[0]; return sheet; } /************************************ シート内のA列の2行目から入力されているissue keyを取得して 二次元配列で取得されるので一次元配列にして返す ************************************/ function get_keys(sheet) { //var col = 2; // B列 var col = getProps("start_col"); var last_row = get_last_row(sheet, col); var rangeA = sheet.getRange(last_row + 1, issue_key_col, rownums, 1); var valuesA = rangeA.getValues(); var keys = array_push_apply(valuesA); Logger.log(keys); return keys; } /************************************ 二次元配列を一次元配列にして返す ************************************/ function array_push_apply(arrays){ for(var i = 1; i < arrays.length; i++){ Array.prototype.push.apply(arrays[0], arrays[i]); } return arrays[0]; } /************************************ issue keyの最後のステータス変更を取得して last_statuses配列に入れて返す ************************************/ function get_last_statuses(keys) { var last_statuses = []; for(var i = 0; i < keys.length; i++) { var key = keys[i]; if(key === '') {// keyがなくなれば console.log('トリガーを削除します' + new Date()) deleteTrigger("run");// トリガーを削除する break; } else { // keyがあれば var latest_status = get_latest_status(key); last_statuses.push(latest_status); } } return last_statuses; } /************************************ issue keyの最後のステータス変更の履歴を取得して返す ************************************/ function get_latest_status(key) { var histories = get_issue_histories(key); var arrays = get_status(histories); var latest_status = arrays[0];// descにした配列の先頭がlatest return latest_status; } /************************************ issueの情報からchangelogのhistoriesを取得して返す ************************************/ function get_issue_histories(key) { var response = get_issue(key); var jobj = JSON.parse(response); var histories = jobj["changelog"]["histories"]; return histories; } /************************************ issueの情報を?expand=changelog付けて取得して返す ************************************/ function get_issue(key) { var token = getProp("token"); var options = { contentType: "application/json", headers: {"Authorization": " Basic " + token} }; var url = ISSUE_URL + key + "?expand=changelog"; var response = UrlFetchApp.fetch(url, options); return response; } /************************************ histories(履歴)の数だけ繰り返す さらに履歴の中のitemsの数だけ繰り返す fieldがstatusならcreatedとfromとtoのオブジェクトを作って arrays配列に入れて descending_arrayで日時を基準に降順にして返す ************************************/ function get_status(histories) { var arrays = []; for(var i = 0; i < histories.length; i++) { var history = histories[i]; var created = format_date(history["created"]); var items = history["items"]; for(var j = 0; j < items.length; j++) { var item = items[j]; var field = item["field"]; if(field === "status") { var obj = {}; obj["created"] = created; obj["from"] = item["fromString"]; obj["to"] = item["toString"]; arrays.push(obj); } } } var desc = descending_array(arrays); return desc; } /************************************ シートに入れる値を作って入れる ************************************/ function create_value(last_statuses, sheet) { var values = []; for(var i = 0; i < last_statuses.length; i++) { var latest_status = last_statuses[i]; if(latest_status === undefined) { values.push(["-", "-", "-"]);// statusの変更履歴がなければ-にする→しないとlatest_status["created"]などがエラーになるのでここで回避 //set_values([["-", "-", "-"]], sheet) } else { var created = latest_status["created"]; var from = latest_status["from"]; var to = latest_status["to"]; values.push([created, from, to]); //set_values([[created, from, to]], sheet) } } return values; } /************************************ シートに値を入れる ************************************/ function set_values(array, sheet){ //var start_col = 2; var start_col = getProps("start_col"); var last_row = get_last_row(sheet, start_col); var start_row = last_row + 1; var num_rows = array.length; var num_cols = array[0].length; var range = sheet.getRange(start_row, start_col, num_rows, num_cols); range.setValues(array); } /************************************ 指定したcol(列)の値が入っている最終行を取得して返す ************************************/ function get_last_row(sheet, col) { var start_row = 1; var num_cols = 1; var sh_last_row = sheet.getLastRow(); var values = sheet.getRange(start_row, col, sh_last_row, num_cols).getValues(); for (var i = values.length - 1; i >= 0; i--) { if (values[i] != "") { break; } } var last_row = i + 1; return last_row; } /************************************ 受け取ったkeyの値をスクリプトのプロパティから返す ************************************/ function getProp(key) { return PropertiesService.getScriptProperties().getProperty(key); } /************************************ 日時をフォーマットして返す ************************************/ function format_date(date) { return Utilities.formatDate(new Date(date), "asia/tokyo", "yyyy-MM-dd HH:mm:ss"); } /************************************ 日時を降順にした配列を返す ************************************/ function descending_array(array) { var descending = array.sort(sorting_desc); return descending; } /************************************ array配列内の日時を降順にする ************************************/ function sorting_desc(a, b){ if(a[0] > b[0]){ return -1; }else if(a[0] < b[0] ){ return 1; }else{ return 0; } } /************************************ トリガーを削除する ************************************/ function deleteTrigger(functionName) { var allTriggers = ScriptApp.getProjectTriggers(); for (var i = 0; i < allTriggers.length; i++) { if (allTriggers[i].getHandlerFunction() == functionName) { ScriptApp.deleteTrigger(allTriggers[i]); break; } } } /************************************ スクリプトのプロパティを読み書きする ************************************/ var ScriptProperties = PropertiesService.getScriptProperties(); function getProps(id) { return ScriptProperties.getProperty(id) } function set_property(key, value){ ScriptProperties.setProperty(key, value); } /************************************ 1行目に自動入力する ************************************/ function set_header(sheet, values){ var start_col = sheet.getLastColumn() + 1; set_property("start_col", start_col); var start_row = 1; var num_rows = 1; var num_cols = values.length; var range = sheet.getRange(start_row, start_col, num_rows, num_cols); range.setValues([values]); } |