社内ツール作成紀行 ~チケットシステム with Webex~

---
この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2021 の１つとして投稿しています。昨年の記事や関連する投稿記事は以下リンクよりご覧いただけます。

2020年版: https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2
2021年版: https://qiita.com/advent-calendar/2021/cisco 
2021年版(2枚目): https://qiita.com/advent-calendar/2021/cisco2

---

# はじめに
社内には検証用のラボがあり、配線の変更や機材の設定などなど、日々のラボワークがたくさんあります。いままでは大まかな管理の仕組みはありつつも、数多くの細やかな作業を体系的に記録し連携することが難しい状況でした。。。:angel:

ラボの自動化をゴールに様々な便利ツールの整備（OSS/自作）をチームで行っていますので、
この記事ではそんな活動の１つとして作成したCisco Webexを活用したチケットシステムについてご紹介します！

# システムの概要
このチケットシステムは社内のGitlabと連携する形で、GitlabのIssue機能を利用して運用していくコンセプトで作成しています。
Gitlab上のプロジェクトと紐付けることで、Issueの作成・編集・コメントに合わせてWebhookをサーバ側で受けることができます。このWebhookの内容をサーバーでパースし、WebexへメッセージとしてPushしていくようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239243/2b30e240-8712-c92e-40a6-85984c82f0f6.png)


- コンテナイメージ：[hanakm/ticket-system-webex](https://hub.docker.com/repository/docker/hanakm/ticket-system-webex/general)
- Gitlab：[hanakamu/ticket-system-webex](https://gitlab.com/hanakamu/ticket-system-webex)

こういったツールを作成する際にどういった言語・フレームワークで作っていくかとっても悩んでしまうのですが、今回は以前によく触っていたNodeJSのフレームワークである　[NestJS](https://nestjs.com/)を使って作ってみました！

## NestJS:cat:
[NestJS](https://nestjs.com/)はNodeJSのサーバーサイドアプリケーション向けのフレームワークです。
特徴として

1. Typescriptにデフォルトで対応
1. CLIツールでひな形を簡単に作成
1. モジュール化による柔軟な機能変更
1. **猫ちゃんかわいい**:cat:

が挙げられます。
特に<del>猫ちゃんかわいい</del>。。機能のモジュール化はコードの汎用性を上げるためにも重要な機能となります。
今回のシステムではWebexを利用するようになっていますが、同じようなメッセージングアプリを利用する際にも対応する部分のみを柔軟に入れ替えることができるのでスムーズな移行を実現できます。

# 使い方
将来的な引継ぎも兼ねて、なるべく煩雑にしたくないという思想でコンテナとして運用できるように設計をしているため、
準備に必要なのは3点

1. Webex Botの作成/API KEYの取得
1. Gitlab Webhookの設定
1. .envファイルの編集（sample.envをもとに）

になります。
コンテナの実行環境がOS上に展開されていること、SaaSのGitlabを利用する際には、Webhookを受信するために対象の機器まで到達性があることを確認してください。
<del>実際にお使いになられる際には、GitlabのGetting Startedのセクションをご参考ください！</del>（絶賛編集中です…:sob:）

ここからは実装にあたって工夫した（行き詰まりかけた）ポイントをご紹介します～

# ポイント①：チケット情報との同期
社内ではWebexのチャットによるやり取りがメインになるため、チケットの状態(Open/Closeなど)を知れると良い。。。という要望が多くありました。
今回はこれに合わせて、Webexのメッセージ編集APIを利用することでリアルタイムにチケット側との同期を取れるような実装をしています。

後述のデータベース連携機能とも合わせて新規メッセージ、アップデートを区別し、それに対応するメッセージをWebex側へ送出します。

- アップデート/状態遷移(Close)の例

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2239243/9da5ae4d-c09f-676f-5480-8547282bbffe.png)


トップのメッセージについている赤マルがクローズ、緑マルがオープンを示しています。
メガホンマークのついたメッセージはチケットに紐づくメッセージに何らかのアップデートがあった際に通知用に送出されます。


# ポイント②: データベース連携
NestJSではORマッパーもプラグインとして入れ込むことができます。今回はTypeORMを利用して、DB側への操作を行っています。

Issue,Commentに連動したWebhookをパースし、DBへの格納を行いますが、Updateに合わせて変動するようなパラメータとの差分を検知できるようにモデルを作成しています。Webex側のメッセージも考えながらでしたのでこの作業にはとても時間がかかりました:angel_tone2:

- （例）チケットの内容を格納するテーブル

```
import {
  Entity,
  Column,
  CreateDateColumn,
  UpdateDateColumn,
  ManyToOne,
  PrimaryColumn,
} from 'typeorm'
import { Issue } from './issue.entity'
import { Room } from './room.entity'

@Entity()
export class IssueMessage {
  @PrimaryColumn()
  id: string

  @ManyToOne(() => Issue, (issue) => issue.messages)
  issue: Issue

  @ManyToOne(() => Room, (room) => room.commentMessages)
  room: Room

  @Column('text')
  text: string

  @CreateDateColumn()
  created_at: Date

  @UpdateDateColumn()
  updated_at: Date
}
```

心残りな点として、NestJSの特徴である入力パラメータのバリデーション([参考リンク](https://docs.nestjs.com/techniques/validation))を活用できていません。。。
このあたりは冬休みの宿題です:snowman2:

# ポイント③: コンテナイメージの作成
とりあえず動けばヨシ！と作り始めたものの、イメージサイズが大きいなァ。。。と悩んでいました。
最初はnodeのオフィシャルイメージにビルドしたファイルを入れ込んで。。。という形で作成していましたが、こちらの[記事](https://zenn.dev/necocoa/articles/nestjs-docker)を参考にイメージを作り直したところ、700MB → 160MB 程度までサイズダウンできました！
「軽いは正義」の信念で生きておりますので、この改善は個人的にとても嬉しかったです:smile:

Googleが提供している[GoogleContainerTools/distroless](https://github.com/GoogleContainerTools/distroless)というイメージをベースにしているのですが、こちらがaptやshellといった機能もない必要最低限のイメージになります。
マルチステージビルドの機能を利用して、NestJSのビルドはnodeの公式イメージ上で行い生成されたdistの中身とnode_modulesをdistroless(node-16)側へコピーするようにしています。これによって実行に必要な最低限のファイルのみを含んだコンテナイメージを生成することができました。

- Dockerfile

```
FROM node:16-slim AS builder

WORKDIR /app
COPY *.json yarn.lock ./
COPY src/ ./src/

RUN yarn install -y --non-interactive --frozen-lockfile &&\
    yarn build

RUN rm -r node_modules &&\
    yarn install --non-interactive --frozen-lockfile --prod


FROM gcr.io/distroless/nodejs:16

WORKDIR /app
COPY --from=builder /app/dist /app/dist
COPY --from=builder /app/node_modules /app/node_modules

EXPOSE 3000
CMD ["dist/main"]
```

# まとめ
この記事では社内ラボの自動化を実現するツールの開発について躓いた点や工夫した点をシェアさせていただきました！
日々の業務ではなかなかソフトウェアの開発に手を出すことは無いのですが、少しでも離れてしまうとあっという間に技術トレンドについていけなくなるなと改めて感じています。。。:angel:
今回でROM専を卒業しましたので、これからは社内ツールだけでなく休日で趣味のソフトウェアDIYができれば、楽しいし力にもなる！！と自分に言い聞かせながら、QiitaへのTips投稿もできればと思っています～:smile:

#免責事項
本サイトおよび対応するコメントにおいて表明される意見は、投稿者本人の個人的意見であり、シスコの意見ではありません。本サイトの内容は、情報の提供のみを目的として掲載されており、シスコや他の関係者による推奨や表明を目的としたものではありません。各利用者は、本Webサイトへの掲載により、投稿、リンクその他の方法でアップロードした全ての情報の内容に対して全責任を負い、本Web サイトの利用に関するあらゆる責任からシスコを免責することに同意したものとします。