GoogleFormとGASを使用してフロントエンドとバックエンドの連携を見直そうとした話

## バグの報告ってどうしていますか？
自分は普段サーバーサイドエンジニアとして働いているのですが、サーバーサイドエンジニアと言えど、いつでもコンソール画面やSQLを眺めている訳ではありません。
普通にブラウザから自社のサイトを見て回ります。

その時に発見してしまう**フロントのバグ**。

- 出るべくしている文言が出ていない
- 画像が出ていない
- 地味なデザイン崩れ
- なぜかボタンが押せない

これってサーバーサイドエンジニアの立場からフロントエンドの人に報告するときは皆さんどうしていますか？
おそらく報告フローがきちんとしている所だと、こういう問題は起こらないと思いますが、うちの会社は報告フローが雑すぎて転職したばっかの自分には分からなすぎた。

1個のWebサイトの中に、各領域（各サービス）があり、それぞれの領域で担当者やリーダーが異なるので、このページのバグは誰に報告すればいいんや...ってそもそも調べるのも面倒だったし時間の無駄感すごかった。

**■今の報告フロー**
1. バグ発見
2. 担当者一覧から担当者を探す
3. チャットで通知
4. タスク立ててもらう

って感じでやってたんですが、なかなか面倒。
担当者を探すのも面倒だし、チャットで直接通知するのも面倒。そして何より、中には話をしたこともなく、顔も知らない人にバグを報告しなきゃいけないので割とコミュ障の自分には精神が擦り減る。
そしてタスクを立てるのか、立てないかはほぼ担当者の権限にも関わらず、報告が個人チャットで飛んでくるとなんとなくタスクを立てなければいけない感も出てくるし、直した後も個別に報告する必要性も出てくる。

とりあえず今の問題点を洗い出すと以下のような感じ。

**■現状のバグ報告フローの問題点**
・個人チャットに連絡することが多い
・バグの報告が多重に起こる場合がある
・チャット報告なので、結局報告したものが流れていく（蓄積ができない

### バグの報告フローを再度考えた

今までの報告フローだと以下のように、優先度低め、高めに限らず直接報告していた。例えば、バグの状況にもよるけど、「SEOに関係のない文言が間違えている」「上に戻るボタンの画像が出ていない」くらいの、そんな重要視しなくてもいいやつ。

**■今の報告フロー**
1. バグ発見
2. 担当者一覧から担当者を探す
3. チャットで通知
4. タスク立ててもらう

タスクを即時で立ててもらっていて、別に急を用するものではないのに圧迫している感じがすごい。優先度にかかわらず仕事をしなくちゃいけない感ある。なぜなら、個人から直接チャットが飛んでくるから。加えて、中にはあんまりチャット見てるのかすら不明な人（無視に近い人）もいるので、この流れ自体本格的に微妙な気がしてきた...。

報告自体、個人チャットから直接連絡くるの自体良くないけど、そもそも報告の仕方自体見直すことを考えた。

- **そもそもチャットでバグ報告するだけは報告が流れてしまう可能性あるしナンセンスでは？**
- **タスク化する・しないは報告者の判断ではなく、担当者のリーダー（個人でタスク化をお願いしていた**
- **バグ自体を蓄積しておきたい**

上記を考慮した結果、以下のような感じにした。

**■新規のバグ報告フロー**
1. バグ発見後、バグ報告フォームからバグを報告
2. フロント担当者は自分の領域のみ週一で確認（症状が中、高は都度確認
3. 優先度を付け、タスク化する必要があるものはリーダーがタスクを立てる
4. タスク立てたらスプレッドシートに記入をする

報告者も担当者にもなるべく負荷がないように。。。
サービスとしてなるべきバグは見つけておきたいし、それを流すだけではなく蓄積しておきたいのはとりあえず解決できそう。


## Google Formでバグ報告フォームを作成して、バグ自体を集計すれば良いんじゃない

結局、チャットに通知しても見ない人は見ない。
あくまでチャットは通知するだけなので、データを蓄積することを頼りにするのはナンセンス。

じゃあ、**Google Formでフォームを作ってバグの報告を蓄積したら良いやん！(Google Formは送信した内容をスプレッドシートに自動集計してくれるので)**

報告されたバグをタスク化するかどうかはフロントに任せて、あくまで他の人は報告をするだけ。

報告されたものを蓄積していくことで、多重報告も無くなるし、何か新規のタスクが降りてくるまで溜まったバグ修正のタスクを消化してもらうのがいいのかなと。

### GoogleFormとスプレッドシートを紐付ける
スプレッドシートを開いたら上部のタブから「挿入」>「フォーム」で開いているスプレッドシートに紐付けられたフォームが生成されます。

GoogleFormの項目は以下で用意。とりあえず誰が起票して（who）、どのページで（where）、どういった現象が起きているのか（what）が分かればいいのかなと。
いつ（When）起票されたのかは、スプレッドシート側でタイムスタンプを作成してくれるのでフォーム側で用意する必要はありません。
※「領域」という項目が各サービスの分野になります

>起票者 *
領域 *
問題のページ
URL *
バグが発生する状況（具体的に *
症状レベル *

<img width="597" alt="バグ報告シート.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/c91db2e5-77b8-1bd7-d55f-e9005081a24d.png">

上記でフォームを作成すると、以下のようにスプレッドシートにカラムが出来上がります。
<img width="1128" alt="Google_スプレッドシート.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/9bd28ead-0b3f-b2a3-65a0-abc80e45f287.png">

フォームから送信された内容がスプレッドシートに溜まっていきます。＼便利／

### バグの症状レベルによって通知する仕組みを作る
フォームからバグを報告しても担当者に見てもらわないといけません。
特に症状レベルが中、高のものは早急に見てもらわないとサービスの質に関わります。

なので、**優先度が中、高のものに関しては担当者宛に通知を飛ばす仕組み**にしました。優先度が「低」のものはスプレッドシートに溜めるだけにして、時間ある際に修正してねって感じです。

### GoogleFormから送信された内容を取得する
まず、GoogleFormから受信した内容を取得する処理が必要なので書いていきます。

GoogleFormから送信された内容を受信するには、GAS側で`トリガー`と呼ばれるものを設定する必要があります。トリガーで以下で記載する特定のメソッドが呼び出される形式にすると、メソッドの引数`e`にレスポンスが入ってきます。GoogleFormの送信内容を取得するには以下の記述方法。

```
e.namedValues['フォームのタイトル'];
```

```javascript
/**
 * フォームから送信された内容を取得し送信する
 */
function doPost(e) {
    let name = e.namedValues['起票者'] ? e.namedValues['起票者'] : Session.getActiveUser();;
    let area = e.namedValues['領域'];
    let page = e.namedValues['問題のページ'];
    let url = e.namedValues['URL'];
    let situ = e.namedValues['バグが発生する状況（具体的に'];
    let level = e.namedValues['症状レベル'];
    let chatId = selectUser(area);

    let reportList = {
        'name' : name,
        'area' : area,
        'page' : page,
        'url' : url,
        'situ' : situ,
        'level' : level,
        'chatId' : chatId
    }

    // 報告内容を送信する
    sendBugAlert(reportList);
}
```

一旦、Form送信で呼び出されるメソッドを書いたので、`トリガー`を設定していきます。
### GoogleFormでトリガーを設定する
スクリプトエディタを開いた状態で、[編集]>[現在のプロジェクトのトリガー]
<img width="335" alt="スクリーンショット 2020-02-25 07.48.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/e308d1e6-9896-9888-547d-82e028816905.png">

設定したトリガーの一覧が出てきますが、最初は何も設定されていない状態です。「トリガーを追加する」ボタンをクリック。
<img width="241" alt="bug_alert_info_-_プロジェクトのトリガー_-_Apps_Script.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/9fb50679-9f14-6f90-8a4b-a275538f647a.png">

トリガーの設定が出てくるので、赤枠の部分を変更します。
**[実行する関数]**　先ほど書いた関数名を選択する
**[イベントの種類]**　フォーム送信時

これでフォーム送信時に、設定した関数が呼ばるようになります。
<img width="712" alt="bug_alert_info_-_プロジェクトのトリガー_-_Apps_Script.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/8e3b3236-fb09-2040-9ac7-3c5cd0ce5d99.png">

### GASでChatworkに通知を送る
僕の会社ではChatworkとSlackを両方使用していますが、今回はChatworkの仕様でコードを書いていきます。
※GASでChatworkを使用するには以下の設定が必要です。（あくまで非公式です
[参考：Google Apps ScriptからChatWork APIを使って投稿する](https://qiita.com/y_minowa/items/39db8ca5bffc9c440caf)

```javascript
function sendBugAlert(reportList) {
    let token = PropertiesService.getScriptProperties().getProperty('CHATWORK_TOKEN');
    let cw = ChatWorkClient.factory({token: token});
    let user = getsUserInfo(reportList.area);
    let body = reportList.level == '高' || reportList.level == '中' ? '[To: ' + user.id + ']' : '';
        body += 
            '[info]' +
            '[title]' + reportList.name + 'さんがバグ報告を上げました！(dance)[/title]' +
            '領域：' + user.area + '\n' +
            '問題のページ：' + reportList.page + '\n' +
            'URL：' + reportList.url + '\n' +
            'バグ内容：' + reportList.situ + '\n' + 
            '症状レベル：' + levelCheck(reportList.level) + '\n' +
            '[/info]';

    cw.sendMessage({
        'room_id' : *********,
        'body' : body
    });
}
```

通知する相手を担当領域別に判別する処理を書く。
`id`にはChatworkの個別IDを
`area`には担当の領域文言を（トップページならTOP　一覧ページなら一覧とか

```javascript
/**
 * 担当者のアカウントを取得する
 */
function getsUserInfo(area) {
    let areaList = {
        'a' : {                
            'id' : '******',
            'area' : 'TOP'
        },         
        'b' : {             
            'id' : '******',
            'area' : '一覧'
        },
        'c' : {               
            'id' : '******',
            'area' : '詳細'
        },
        'd' : {             
            'id' : '******',
            'area' : 'hoge'
        },
        'e' : {             
            'id' : '******',
            'area' : 'fuga'
        },      
        'f' : {              
            'id' : '******',
            'area' : 'saga'
        },
        'g' : {              
            'id' : '******',
            'area' : 'vega'
        },       
        'h' : {          
            'id' : '******',
            'area' : 'baga'
        },
        'i' : {             
            'id' : '******',
            'area' : 'naga'
        },      
        'j' : {              
            'id' : '******',
            'area' : 'maga'
        },       
        'k' : {             
            'id' : '******',
            'area' : 'aaga'
        },      
        'l' : {               
            'id' : '******',
            'area' : 'eega',
        } 
    }
    return areaList[area];
}
```

バグの症状レベルによって絵文字を変更する（これはただの遊び心

```javascript
function levelCheck(lev) {
    if (lev == '低') {
        lev += '(think)';
    }

    if (lev == '中') {
        lev += '(anger)';
    }

    if (lev == '高') {
        lev += '(devil)';
    }
    return lev;
}
```

実際にGoogleFormから送信されるとChatwork側に以下のように送信されます。
ちゃんと症状が「中、高」の場合は、`To`が付くので担当者に通知が飛ぶようになります。「低」のものはただルームに飛ばされるだけなのでミュートにしてもらえれば基本は通知は飛びません。
<img width="514" alt="Chatwork_-_フロント連携.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253969/66276dc1-d58e-bd03-eedc-396b62a45c5f.png">

#### これ導入して結局どうなった？

結論から言うとまだ試験的運用なので結果を出せてはいません。（出してから記事を書けばよかったかな。。。
自分的に工夫して上記のような運用方法を考えてみましたが、皆さんの会社ではどういった感じでサーバーサイドとフロントエンドの連携をしているでしょうか。何か取り組んでることを教えてくださいまし。

にしてもGASって本当に便利やな。。。