JavaScript
jQuery
backlog
bookmarklet

Backlogのチケット本文とコメントに埋まっているチケットIDのステータスを調べるブックマークレット

JBUGのもくもく会で作ったブックマークレットを公開します。誰かの役に立ちますように。

これは何ですか?

ヌーラボさんのBacklogのチケットのステータスを調べるブックマークレットです。
今、ブラウザで開いているチケットの本文とコメントに書かれているチケットID らしきもの を拾い、ステータスを調べて、そのチケットのHTMLの中に表示します。

2018/6/14 変更点
/api/v2/projectsをコールして、APIキーのユーザにとって有効なプロジェクトキーを取ってきて、チケットIDを探すように修正しました。

なぜ作ったのか?

自分の環境では、チケットの本文やチケットコメントに別のプロジェクトのチケットIDが貼られていることがあるのですが、チケットの状態を画面を開いて調べるのが面倒だったので、一発で表示できるようにしてみました。
(チケットにチケットIDを貼らない・書かない、親子チケット使おう、というのはいったん置いときました、とても変態的ソリューションです)

使っているもの

BacklogのAPI
jQuery
ブックマークレット

画面イメージ

本文の枠には、本文内のチケットとコメントのチケットのスタータスが表示されます。
コメントには、そのコメントに含まれているチケットのステータスが表示されます。

実行前の画面

チケット本体
image.png

コメント欄
image.png

実行後の画面

チケット本体
image.png

コメント欄
image.png

画面の表示は変わりますが、コメントを編集しているわけではないのです。再表示(F5)すると元の状態に戻ります。

作成したコード

リファクタリングして117行に短くなりました。

javascript:(
    function(_backlog_url, _backlog_api_key, undefined){

        console.log('jquery version is %s', $.fn.jquery);
        if( $('div.comment-item.is_collapsed').length >0){
            alert('コメントが閉じてるので開いて');
            return;
        }

        var projects = [];

        backlog_projects( function (param, backlog_result){
            for(p in backlog_result){
                projects.push(backlog_result[p].projectKey);
            }
            console.log('backlog projects')
            console.log(projects);


            console.log('--process description of this issue')
            description = $('div.ticket__description').text();
            issues_in_desc = find_backlog_key(projects, description);
            console.log(issues_in_desc);

            for( i in issues_in_desc ){
                backlog_issue(issues_in_desc[i], '', overwrite_issue_status );
            }

            console.log('--process comment of this issue.')
            issue_key = find_backlog_key(projects, window.location.href);
            backlog_comments(issue_key[0], process_comments);

        });



        function process_comments(params, backlog_result){
            //console.log(backlog_result.length);
            for(c in backlog_result){
                //console.log(backlog_result[c]);

                issue_keys = find_backlog_key(projects, backlog_result[c].content);
                if( issue_keys.length > 0){
                    console.log(issue_keys);
                    for(i in issue_keys){
                        backlog_issue(issue_keys[i], backlog_result[c].id, overwrite_issue_status );
                    }
                }
            }
        }

        function overwrite_issue_status(params,backlog){
            //console.log(params);
            console.log("key:%s id:%s status:%s", params.issue_key, params.comment_id, backlog.status.id);
            data = '<span class="status status--' + backlog.status.id + '">'+ backlog.status.name + '</span> ' + params.issue_key + ' : ' + backlog.summary +'<br>';
            $('div#issueDescription').append(data);
            if( params.comment_id !=""){
                $('div#comment-'+params.comment_id).append(data);
            }
        }
        function find_backlog_key(projects, s){
            var ans = [];
            for(p in projects){
                ptn = '(' + projects[p] + '-[0-9]+)';
                reg = new RegExp(ptn, 'g');
                while ((m = reg.exec(s)) != null) {
                    ans.push(m[1]);
                }
            }
            return ans;
        }

        function backlog_issue(issue_key,comment_id, callback){
            url = "https://"+_backlog_url+"/api/v2/issues/"+issue_key+"?apiKey="+_backlog_api_key;
            params = {};
            params.issue_key=issue_key;
            params.comment_id = comment_id;
            backlog_api(url,params, callback);
        }

        function backlog_comments(issue_key,callback){
            url = "https://"+_backlog_url+"/api/v2/issues/"+issue_key+"/comments?apiKey="+_backlog_api_key;
            params = {};
            params.issue_key=issue_key;
            backlog_api(url,params, callback);
        }
        function backlog_projects(callback){
            url = "https://"+_backlog_url+"/api/v2/projects?apiKey="+_backlog_api_key;
            params = {};
            backlog_api(url,params, callback);
        }
        function backlog_api(url, params, callback){
            $.ajax({
                type: "get",
                url: url,
                timeout: 20000,
                cache: false,
                async: true,
                data: {},
                dataType: 'json'
            })
            .done(function (response, textStatus, jqXHR) {
                console.log(response);
                if (response.status === "err") {
                    console.error("err: " + response.msg);
                } else {
                    callback(params, response);
                }
            })
            .fail(function (jqXHR, textStatus, errorThrown) {
                console.log(jqXHR);
            })
            .always(function (data_or_jqXHR, textStatus, jqXHR_or_errorThrown) {
            });
        }
    }
)("xxx.backlog.com","YOUR API KEY");

1

Closure Compiler後のコード

コンパイルしたコードは長いので消しました。
(最後の引数2つを自分の環境に合わせると動くはず。。) 2

手抜きしたところ

1) プロジェクトキーのマッチング

正規表現 [A-Z_]+-[0-9]+ でプロジェクトキーっぽいものを探してます。雑ですね。チケットのコメントにUTF-8って書いてあるとチケットIDと誤解します。

2018/6/14 ちゃんとやりました。

2) 本文(description)を取る部分

/api/v2/issuesで取り出す方法もありましたが、ajaxの非同期待ちが面倒だったので手元のHTMLから取り出しました。

3)チケットの並び順と重複

まったくプログラムしていないので、その日の気分で並ぶと思います。

注意事項

APIのキーは各自で取得してください。APIキーを発行したユーザが参照できないプロジェクトのステータスは表示できません。
APIのキーとURLは公開しないように気を付けてください。Backlogのデータが丸見えになります
BacklogのHTMLのclassやidが変わると動かなくなる部分がありますが、手直ししてください。
ご利用は自己責任でお願いします。

参考にした資料

https://developer.nulab-inc.com/ja/docs/backlog/


  1. コードはgithubに上げるようにしないと。 

  2. Closure Compilerする方法はこの辺参照