# 機能設計書 92-プロジェクトWebhook

## 概要

本ドキュメントは、GitLabにおけるプロジェクトWebhook機能の設計仕様を記載する。本機能は、プロジェクト内で発生した各種イベント（プッシュ、マージリクエスト、イシュー作成等）を外部のHTTPエンドポイントに通知するための設定・管理・実行機能を提供する。

### 本機能の処理概要

プロジェクトWebhook機能は、プロジェクト内のイベント発生時に指定されたURLへHTTP POSTリクエストを送信し、外部システムとの連携を実現する。CI/CDパイプライン、チャットツール、カスタム自動化スクリプトなど、様々な外部サービスとの統合に使用される。

**業務上の目的・背景**：現代のDevOps環境では、GitLabでの活動を他のツールやシステムにリアルタイムで通知する必要がある。Webhookは、プル型（ポーリング）ではなくプッシュ型の通知を実現し、即時性の高い連携を可能にする。これにより、イベント駆動型のワークフロー自動化、外部システムへの即時データ同期、カスタム通知・アラート機能の構築が実現できる。

**機能の利用シーン**：本機能は以下のシーンで利用される。
- コードプッシュ時のCI/CDパイプライントリガー
- イシュー作成時のチケット管理システムへの連携
- マージリクエストの状態変更時のSlack/Teams通知
- デプロイ完了時の監視システムへの通知
- Wikiページ更新時のドキュメント管理システムへの同期

**主要な処理内容**：
1. Webhookの登録・編集・削除（CRUD操作）
2. 対象イベントの選択と設定
3. イベント発生時のHTTPリクエスト送信
4. リクエスト/レスポンスのログ記録
5. 失敗時の自動無効化とリトライ処理
6. レート制限と再帰検出による保護

**関連システム・外部連携**：外部HTTPエンドポイント（任意のWebサービス）、Sidekiq（非同期処理）、SSL/TLS（暗号化通信）

**権限による制御**：Webhookの閲覧は`read_web_hook`権限、作成・編集・削除は`admin_web_hook`権限が必要。これらは通常、プロジェクトのMaintainer以上のロールに付与される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 121 | Webhook一覧 | 主機能 | Webhook設定の一覧表示 |
| 122 | Webhook編集 | 主機能 | Webhook設定の編集 |
| 133 | Hookログ詳細 | 参照画面 | Webhookログの詳細表示 |

## 機能種別

CRUD操作 / イベント通知 / 外部システム連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | String | Yes | 通知先URL | 8192文字以内、パブリックURL |
| token | String | No | 認証用シークレットトークン | 8192文字以内、改行なし |
| name | String | No | Webhook名 | 255文字以内 |
| description | String | No | 説明 | 2048文字以内 |
| push_events | Boolean | No | プッシュイベントを通知 | - |
| push_events_branch_filter | String | No | ブランチフィルタ | 正規表現またはワイルドカード |
| branch_filter_strategy | String | No | ブランチフィルタ戦略 | wildcard/regex/all_branches |
| issues_events | Boolean | No | イシューイベントを通知 | - |
| confidential_issues_events | Boolean | No | 機密イシューイベントを通知 | - |
| merge_requests_events | Boolean | No | MRイベントを通知 | - |
| tag_push_events | Boolean | No | タグプッシュイベントを通知 | - |
| note_events | Boolean | No | コメントイベントを通知 | - |
| confidential_note_events | Boolean | No | 機密コメントイベントを通知 | - |
| job_events | Boolean | No | ジョブイベントを通知 | - |
| pipeline_events | Boolean | No | パイプラインイベントを通知 | - |
| wiki_page_events | Boolean | No | Wikiイベントを通知 | - |
| deployment_events | Boolean | No | デプロイイベントを通知 | - |
| feature_flag_events | Boolean | No | フィーチャーフラグイベントを通知 | - |
| releases_events | Boolean | No | リリースイベントを通知 | - |
| milestone_events | Boolean | No | マイルストーンイベントを通知 | - |
| emoji_events | Boolean | No | 絵文字イベントを通知 | - |
| resource_access_token_events | Boolean | No | アクセストークン期限イベントを通知 | - |
| enable_ssl_verification | Boolean | No | SSL証明書検証を有効化 | デフォルト: true |
| custom_webhook_template | String | No | カスタムペイロードテンプレート | 4096文字以内 |
| url_variables | Hash | No | URL変数（テンプレート置換用） | JSON形式 |
| custom_headers | Hash | No | カスタムHTTPヘッダー | JSON形式 |

### 入力データソース

- 画面入力（Webhook設定フォーム）
- REST API（/api/v4/projects/:id/hooks）
- GraphQL API（ProjectHooksResolver）
- イベントデータ（各種サービスからのトリガー）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | Webhook ID |
| url | String | 通知先URL |
| name | String | Webhook名 |
| description | String | 説明 |
| project_id | Integer | プロジェクトID |
| push_events | Boolean | プッシュイベント設定 |
| *_events | Boolean | 各種イベント設定 |
| enable_ssl_verification | Boolean | SSL検証設定 |
| created_at | DateTime | 作成日時 |
| disabled_until | DateTime | 無効化解除予定日時 |
| alert_status | String | アラート状態（executable/disabled/temporarily_disabled） |

### 出力先

- 画面表示（Webhook一覧・詳細）
- REST API レスポンス
- GraphQL レスポンス
- 外部HTTPエンドポイント（イベント通知時）
- WebHookLogテーブル（実行ログ）

## 処理フロー

### 処理シーケンス

```
1. Webhook登録
   └─ WebHooks::CreateServiceでバリデーション・保存

2. イベント発生
   └─ 該当プロジェクトのフックを取得（TriggerableHooks）
   └─ イベントタイプに応じたフィルタリング

3. 非同期実行
   └─ WebHookWorkerにジョブをエンキュー
   └─ レート制限チェック
   └─ 再帰検出チェック

4. HTTP送信（WebHookService#execute）
   └─ ペイロード構築（カスタムテンプレート適用）
   └─ ヘッダー構築（認証トークン、カスタムヘッダー）
   └─ Gitlab::HTTP.postでリクエスト送信

5. レスポンス処理
   └─ ログ記録（WebHooks::LogExecutionService）
   └─ 失敗時の自動無効化（AutoDisabling）

6. ログ保存
   └─ WebHookLogテーブルへ記録
   └─ リクエスト/レスポンス情報の保存
```

### フローチャート

```mermaid
flowchart TD
    A[イベント発生] --> B{Webhook存在?}
    B -->|No| C[終了]
    B -->|Yes| D{イベント対象?}
    D -->|No| C
    D -->|Yes| E{実行可能?}
    E -->|No| F[スキップ・ログ出力]
    E -->|Yes| G{レート制限?}
    G -->|Yes| F
    G -->|No| H{再帰検出?}
    H -->|Yes| F
    H -->|No| I[WebHookWorkerへエンキュー]
    I --> J[HTTPリクエスト送信]
    J --> K{成功?}
    K -->|Yes| L[成功ログ記録]
    K -->|No| M{リトライ可能?}
    M -->|Yes| N[失敗カウント増加]
    M -->|No| O[Webhook無効化]
    N --> P[エラーログ記録]
    O --> P
    L --> C
    P --> C
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-92-01 | プロジェクト制限 | プロジェクトごとのWebhook数上限あり | Webhook作成時 |
| BR-92-02 | 自動無効化 | 連続失敗時にWebhookを自動的に無効化 | 送信失敗時 |
| BR-92-03 | サイレントモード | サイレントモード中はWebhook送信を抑止 | 全送信時 |
| BR-92-04 | ローカルアドレス制限 | デフォルトでlocalhostやプライベートIPへの送信は禁止 | URL設定時 |
| BR-92-05 | レート制限 | 過剰な送信を防ぐためレート制限を適用 | 送信時 |
| BR-92-06 | 再帰防止 | Webhook起因のイベントによる無限ループを防止 | 送信時 |
| BR-92-07 | ブランチフィルタ | 指定したブランチのみを対象とする | pushイベント時 |

### 計算ロジック

**タイムアウト計算**：
```ruby
timeout = Gitlab.config.gitlab.webhook_timeout # デフォルト: 10秒
```

**レスポンスサイズ制限**：
```ruby
max_bytes = Gitlab::CurrentSettings.max_http_response_size_limit.megabytes
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Webhook作成 | web_hooks | INSERT | 新規Webhook設定の登録 |
| Webhook更新 | web_hooks | UPDATE | Webhook設定の変更 |
| Webhook削除 | web_hooks | DELETE | Webhook設定の削除 |
| ログ記録 | web_hook_logs | INSERT | 実行ログの記録 |
| 状態更新 | web_hooks | UPDATE | disabled_until, recent_failuresの更新 |

### テーブル別操作詳細

#### web_hooks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | url, token, project_id, *_events | フォーム入力値 | 暗号化して保存 |
| UPDATE | url, token, *_events, disabled_until | フォーム入力値/自動更新 | - |
| DELETE | - | id条件 | 関連ログも削除 |

#### web_hook_logs

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | web_hook_id, trigger, url, request_headers, request_data, response_headers, response_body, response_status, execution_duration | リクエスト/レスポンス情報 | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | タイムアウト | レスポンスがタイムアウト時間内に返らない | 失敗カウント増加、リトライ |
| - | 接続エラー | 接続先に到達できない | 失敗カウント増加、リトライ |
| - | SSL検証エラー | SSL証明書が無効 | SSL検証無効化または証明書修正 |
| - | レート制限 | 送信頻度が上限超過 | 待機後に自動再開 |
| - | 再帰検出 | Webhook起因の無限ループを検出 | 送信をブロック |
| - | サイズ超過 | リクエストボディが25MB超 | ペイロードの削減 |

### リトライ仕様

WebHookServiceはリトライを行わず、失敗時は失敗カウントを増加させる。連続失敗が閾値を超えるとWebhookは一時的に無効化される（AutoDisabling機能）。

## トランザクション仕様

Webhook作成・更新はActiveRecordのトランザクション内で実行される。ログ記録は非同期ワーカー（WebHooks::LogExecutionWorker）で行われるため、メイン処理とは独立したトランザクションとなる。

## パフォーマンス要件

- HTTPリクエストタイムアウト：10秒（設定可能）
- リクエストボディサイズ上限：25MB
- レスポンスボディ保存上限：8KB
- レスポンスヘッダー保存上限：50個、各1KB
- 非同期処理：Sidekiqワーカーによる非同期実行

## セキュリティ考慮事項

- **URL暗号化**：URLはAES-256-GCMで暗号化して保存
- **トークン暗号化**：認証トークンはAES-256-GCMで暗号化して保存
- **X-Gitlab-Token**：認証用ヘッダーとしてトークンを送信
- **SSL検証**：デフォルトで有効、オプションで無効化可能
- **ローカルアドレス制限**：管理者設定でのみ許可
- **Idempotency-Key**：重複実行防止用のヘッダー
- **再帰検出ヘッダー**：無限ループ防止用のヘッダー

## 備考

- カスタムテンプレートは`{{field.subfield}}`形式で変数参照可能
- URL変数は`{variable_name}`形式でURL内に埋め込み可能
- テスト送信機能でWebhookの動作確認が可能

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

Webhookのモデル構造と関連するconcernを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | project_hook.rb | `app/models/hooks/project_hook.rb` | ProjectHookモデル、利用可能なフック種別 |
| 1-2 | web_hook.rb | `app/models/hooks/web_hook.rb` | WebHook基底クラス |
| 1-3 | hook.rb | `app/models/concerns/web_hooks/hook.rb` | Webhookの主要ロジック、暗号化、バリデーション |

**読解のコツ**: `ProjectHook`は`WebHook`を継承し、`WebHook`は`WebHooks::Hook`をincludeしている。主要なロジックは`WebHooks::Hook`concernに集約されている。

#### Step 2: エントリーポイントを理解する

Webhookの管理画面コントローラーとAPIエンドポイント。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hooks_controller.rb | `app/controllers/projects/hooks_controller.rb` | 画面からのCRUD操作 |
| 2-2 | hook_actions.rb | `app/controllers/concerns/web_hooks/hook_actions.rb` | 共通のCRUDアクション |
| 2-3 | project_hooks.rb | `lib/api/project_hooks.rb` | REST API実装 |

**主要処理フロー**:
1. **7-8行目（hooks_controller.rb）**: 権限チェック（before_action）
2. **17-24行目**: テスト送信機能
3. **20-33行目（hook_actions.rb）**: Webhook作成処理
4. **35-52行目**: Webhook更新処理

#### Step 3: イベントトリガー機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | triggerable_hooks.rb | `app/models/concerns/triggerable_hooks.rb` | イベント種別とトリガー管理 |

**主要処理フロー**:
- **9-14行目**: `hooks_for(trigger)` - 指定イベントに対応するフックを取得
- **25-47行目**: `available_triggers` - 利用可能なトリガーと対応するイベントカラム

#### Step 4: Webhook実行サービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | web_hook_service.rb | `app/services/web_hook_service.rb` | HTTPリクエスト送信の実装 |

**主要処理フロー**:
- **32-38行目**: サイズ制限の定数定義
- **51-62行目**: コンストラクタ、オプション設定
- **68-115行目**: `execute` - 同期実行処理
- **117-130行目**: `async_execute` - 非同期実行処理
- **146-154行目**: `make_request` - HTTPリクエスト送信
- **211-225行目**: `build_headers` - リクエストヘッダー構築
- **293-310行目**: `request_payload` - ペイロード構築（カスタムテンプレート適用）

#### Step 5: 自動無効化機能を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | auto_disabling.rb | `app/models/concerns/web_hooks/auto_disabling.rb` | 連続失敗時の自動無効化ロジック |

### プログラム呼び出し階層図

```
Projects::HooksController
    |
    +-- WebHooks::HookActions (concern)
    |       |
    |       +-- WebHooks::CreateService#execute
    |       +-- WebHooks::DestroyService#execute
    |
    +-- TestHooks::ProjectService#execute (テスト送信)
            |
            +-- WebHookService#execute
                    |
                    +-- Gitlab::HTTP.post
                    +-- WebHooks::LogExecutionService

[イベント発生時]
Project#trigger_hooks
    |
    +-- ProjectHook.hooks_for(:push_hooks)
    +-- WebHook#async_execute
            |
            +-- WebHookWorker.perform_async
                    |
                    +-- WebHookService#execute
```

### データフロー図

```
[入力]                      [処理]                           [出力]

フォーム入力        ---->   HooksController              ---->  DB保存
(url, token等)              WebHooks::CreateService            (web_hooks)

イベントデータ      ---->   Project#trigger_hooks        ---->  外部HTTP
(push, MR等)                WebHookService#execute             エンドポイント
                                    |
                                    v
                            WebHooks::LogExecutionWorker ---->  DB保存
                                                               (web_hook_logs)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hooks_controller.rb | `app/controllers/projects/hooks_controller.rb` | ソース | 画面コントローラー |
| hook_actions.rb | `app/controllers/concerns/web_hooks/hook_actions.rb` | ソース | CRUD共通処理 |
| project_hook.rb | `app/models/hooks/project_hook.rb` | ソース | Webhookモデル |
| hook.rb | `app/models/concerns/web_hooks/hook.rb` | ソース | Webhook基本機能 |
| triggerable_hooks.rb | `app/models/concerns/triggerable_hooks.rb` | ソース | トリガー管理 |
| auto_disabling.rb | `app/models/concerns/web_hooks/auto_disabling.rb` | ソース | 自動無効化 |
| web_hook_service.rb | `app/services/web_hook_service.rb` | ソース | HTTP送信サービス |
| project_hooks.rb | `lib/api/project_hooks.rb` | ソース | REST API |
| project_hook.rb | `lib/api/entities/project_hook.rb` | ソース | APIエンティティ |
| project_hook_type.rb | `app/graphql/types/web_hooks/project_hook_type.rb` | ソース | GraphQL型定義 |
