# 機能設計書 143-Hooks（Integrations）

## 概要

本ドキュメントは、Chatwootにおける統合用Hooks（Integrations::Hook）モデルの設計書である。Slack、Linear、Dialogflow、Dyte、Shopifyなどの外部サービスとの連携を管理するためのデータモデルと設定管理機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：カスタマーサポート業務では、単一のツールだけでなく、複数のサービスを組み合わせて効率的に運用する必要がある。Hooksモデルは、Chatwootと外部サービス（Slack、Linear、Dialogflow等）との連携を抽象化し、統一的なインターフェースで管理することを可能にする。これにより、新しい統合サービスの追加や既存連携の管理が容易になり、ビジネスニーズに応じた柔軟なカスタマイズが実現できる。

**機能の利用シーン**：
- Slackとの連携を設定して会話通知をSlackチャネルに送信する
- Dialogflowボットをインボックスに接続して自動応答を実現する
- Linear統合でカスタマーサポートの課題管理を効率化する
- Dyte統合でビデオ通話機能を有効にする
- Shopify統合で顧客の注文情報を表示する

**主要な処理内容**：
1. 統合アプリケーション（Integrations::App）の定義と管理
2. Hooks（接続設定）のCRUD操作
3. アクセストークンの暗号化保存
4. hook_type（account/inbox）によるスコープ管理
5. 設定値のJSONスキーマバリデーション
6. フィーチャーフラグによる機能制御

**関連システム・外部連携**：
- Slack API（OAuth2認証、メッセージ送信）
- Linear API（GraphQL、課題管理）
- Google Dialogflow（自然言語処理）
- Dyte API（ビデオ会議）
- Shopify API（Eコマース）
- Notion API（データベース連携）

**権限による制御**：
- Hook（統合）の作成・編集・削除は管理者権限が必要
- 一部の統合はフィーチャーフラグによる制御がある（例：linear_integration、crm_integration）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 65 | 連携アプリ一覧画面 | 主画面 | 連携アプリ（Hook）の一覧表示 |
| 66 | ダッシュボードアプリ設定 | 参照画面 | ダッシュボードアプリの設定 |
| 72 | 連携詳細画面 | 参照画面 | 各連携の詳細設定 |

## 機能種別

CRUD操作 / 設定管理 / データ連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| app_id | String | Yes | 統合アプリID（slack, linear等） | apps.ymlで定義されたIDのみ |
| account_id | Integer | Yes | アカウントID | 存在するアカウント |
| inbox_id | Integer | 条件付き | インボックスID | hook_type='inbox'時は必須 |
| access_token | String | No | 外部サービスのアクセストークン | - |
| settings | JSONB | No | 統合固有の設定値 | settings_json_schemaに従う |
| status | Integer | No | ステータス（enabled/disabled） | enum値 |

### 設定値スキーマ（例：Dyte）

```json
{
  "type": "object",
  "properties": {
    "api_key": { "type": "string" },
    "organization_id": { "type": "string" }
  },
  "required": ["api_key", "organization_id"],
  "additionalProperties": false
}
```

### 入力データソース

管理画面の統合設定フォームからの入力、OAuth認証コールバック

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | HookのID |
| app_id | String | 統合アプリID |
| account_id | Integer | 所属アカウントID |
| inbox_id | Integer | インボックスID（inbox type時） |
| access_token | String | 暗号化されたアクセストークン |
| settings | JSONB | 統合固有設定 |
| status | Integer | enabled(1) / disabled(0) |
| hook_type | Integer | account(0) / inbox(1) |
| reference_id | String | 外部サービスの参照ID |
| created_at | DateTime | 作成日時 |
| updated_at | DateTime | 更新日時 |

### 出力先

- integrations_hooksテーブル（データベース）
- APIレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. 統合アプリ選択
   └─ Integrations::App.allから利用可能なアプリを取得

2. 認可チェック
   └─ feature_allowed?でフィーチャーフラグ確認
   └─ active?で環境設定（API KEY等）確認

3. Hook作成
   └─ Integrations::Hook.new(params)
   └─ ensure_hook_type（アプリ設定からhook_typeを自動設定）
   └─ validate_settings_json_schema（設定値バリデーション）
   └─ ensure_feature_enabled（フィーチャーフラグ確認）

4. トークン暗号化
   └─ has_secure_token :access_token
   └─ encrypts :access_token（暗号化設定時）

5. 後処理
   └─ trigger_setup_if_crm（CRM統合時のセットアップ）
```

### フローチャート

```mermaid
flowchart TD
    A[統合アプリ選択] --> B{アプリがアクティブ?}
    B -->|No| C[利用不可表示]
    B -->|Yes| D{フィーチャーフラグ有効?}
    D -->|No| E[機能無効エラー]
    D -->|Yes| F[Hook作成フォーム表示]
    F --> G[パラメータ入力]
    G --> H{設定値バリデーション}
    H -->|失敗| I[バリデーションエラー]
    H -->|成功| J[Hook保存]
    J --> K{CRM統合?}
    K -->|Yes| L[Crm::SetupJob実行]
    K -->|No| M[完了]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-143-01 | アプリ一意性 | allow_multiple_hooks=falseのアプリはアカウント内で1つのみ | Hook作成時 |
| BR-143-02 | hook_type自動設定 | アプリ設定のhook_typeに従って自動設定 | Hook作成時 |
| BR-143-03 | inbox必須条件 | hook_type='inbox'の場合はinbox_idが必須 | Hook作成時 |
| BR-143-04 | 設定値スキーマ検証 | settings_json_schemaが定義されている場合は検証必須 | Hook作成・更新時 |
| BR-143-05 | フィーチャーフラグ | feature_flagが定義されている場合はアカウントで有効である必要がある | Hook作成時 |
| BR-143-06 | トークン暗号化 | 暗号化設定時はaccess_tokenを暗号化して保存 | Hook保存時 |
| BR-143-07 | CRMセットアップ | CRM統合（leadsquared等）作成時は自動セットアップを実行 | CRM Hook作成後 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | integrations_hooks | SELECT | アカウントのHook一覧取得 |
| 作成 | integrations_hooks | INSERT | 新規Hook作成 |
| 更新 | integrations_hooks | UPDATE | Hook設定更新 |
| 削除 | integrations_hooks | DELETE | Hook削除 |
| 無効化 | integrations_hooks | UPDATE | status=disabledに更新 |

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

#### integrations_hooks

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | account_id = Current.account.id | 一覧取得 |
| SELECT | * | app_id = 'slack' AND account_id = ? | 特定アプリのHook取得 |
| INSERT | app_id, account_id, inbox_id, settings, access_token, status, hook_type | リクエストパラメータ | 新規作成 |
| UPDATE | settings, status, reference_id | 更新パラメータ | 設定更新 |
| UPDATE | status | 'disabled' | 無効化 |
| DELETE | - | id = ? | 物理削除 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Unprocessable Entity | 設定値がスキーマに違反 | 正しい形式で設定値を入力 |
| 422 | Unprocessable Entity | フィーチャーフラグが無効 | 管理者にフィーチャー有効化を依頼 |
| 422 | Unprocessable Entity | 重複Hook（allow_multiple_hooks=false時） | 既存のHookを削除または更新 |
| 422 | Unprocessable Entity | inbox_id未指定（inbox type時） | インボックスを選択 |
| 404 | Not Found | 指定されたHookが存在しない | 正しいIDを指定 |

### リトライ仕様

Hook作成自体にはリトライ仕様は存在しない

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

- 各CRUD操作は単一トランザクションで処理
- after_createコールバックでCRMセットアップジョブを非同期実行

## パフォーマンス要件

- 一覧取得: 100ms以内
- 作成・更新: 500ms以内

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

- access_tokenは暗号化して保存（Chatwoot.encryption_configured?時）
- attr_readonly :app_id, :account_id, :inbox_id, :hook_type で作成後の変更を防止
- has_secure_token :access_tokenでトークン自動生成
- settings内の機密情報（api_key等）もJSONBとして暗号化対象

## 備考

- Integrations::AppはYAML設定（config/integration/apps.yml）から読み込まれる
- ReauthorizableモジュールでOAuth再認証フローをサポート
- 各統合アプリ固有のコントローラーとサービスが存在する

---

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

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

### 推奨読解順序

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

Hookモデルと統合アプリ定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hook.rb | `app/models/integrations/hook.rb` | Hookモデルの定義、バリデーション、enum |
| 1-2 | apps.yml | `config/integration/apps.yml` | 統合アプリの設定定義 |
| 1-3 | app.rb | `app/models/integrations/app.rb` | 統合アプリのラッパークラス |

**読解のコツ**:
- **hook.rb 17-40行目**: アソシエーション、バリデーション、enumの定義
- **hook.rb 20行目**: `attr_readonly`で変更不可属性を指定
- **hook.rb 25行目**: `encrypts :access_token`で暗号化設定
- **hook.rb 32行目**: `validates :app_id, uniqueness`でアプリ単位の一意性制御
- **hook.rb 36-37行目**: enumでstatus(enabled/disabled)とhook_type(account/inbox)を定義
- **apps.yml**: 各統合アプリのid, hook_type, settings_json_schema等を確認

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

統合アプリの共通処理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | app.rb | `app/models/integrations/app.rb` | 統合アプリの動的生成 |

**主要処理フロー**:
1. **40-48行目**: `action`メソッドでSlack/Linear用のOAuth URLを構築
2. **51-65行目**: `active?`メソッドで環境設定の有無を確認
3. **81-89行目**: `enabled?`メソッドでHookの存在確認
4. **104-117行目**: クラスメソッドで全アプリ取得、特定アプリ検索

#### Step 3: バリデーションロジックを理解する

Hookモデルのバリデーションを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hook.rb | `app/models/integrations/hook.rb` | バリデーションメソッド |

**主要処理フロー**:
- **84-86行目**: `ensure_feature_enabled`でフィーチャーフラグ確認
- **88-90行目**: `ensure_hook_type`でアプリ設定からhook_type自動設定
- **92-96行目**: `validate_settings_json_schema`でJSONスキーマバリデーション
- **98-105行目**: `trigger_setup_if_crm`でCRM統合時のセットアップジョブ実行

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

```
Integrations::App
    │
    ├─ .all / .find
    │      └─ APPS_CONFIG (apps.yml)
    │
    ├─ #active?
    │      └─ GlobalConfigService.load
    │      └─ account.feature_enabled?
    │
    └─ #enabled?
           └─ account.hooks.exists?(app_id: id)

Integrations::Hook
    │
    ├─ before_validation
    │      └─ ensure_hook_type
    │             └─ app.params[:hook_type]
    │
    ├─ validate
    │      ├─ validate_settings_json_schema
    │      │      └─ JSONSchemer.schema
    │      └─ ensure_feature_enabled
    │             └─ feature_allowed?
    │
    └─ after_create
           └─ trigger_setup_if_crm
                  └─ Crm::SetupJob.perform_later
```

### データフロー図

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

統合設定画面           ───▶ Integrations::App           ───▶ integrations_hooks
(app_id, settings,              │                              テーブル
 access_token)                  ▼                                │
                         Integrations::Hook                      ▼
                               │                          外部サービス
                               ├─ バリデーション            API連携
                               │   (スキーマ検証)
                               │
                               └─ トークン暗号化
                                   保存
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hook.rb | `app/models/integrations/hook.rb` | モデル | Hookデータモデル定義 |
| app.rb | `app/models/integrations/app.rb` | モデル | 統合アプリラッパー |
| apps.yml | `config/integration/apps.yml` | 設定 | 統合アプリ定義 |
| reauthorizable.rb | `app/models/concerns/reauthorizable.rb` | Concern | 再認証フロー |
| slack_controller.rb | `app/controllers/api/v1/accounts/integrations/slack_controller.rb` | コントローラー | Slack統合API |
| linear_controller.rb | `app/controllers/api/v1/accounts/integrations/linear_controller.rb` | コントローラー | Linear統合API |
| dyte_controller.rb | `app/controllers/api/v1/accounts/integrations/dyte_controller.rb` | コントローラー | Dyte統合API |
| shopify_controller.rb | `app/controllers/api/v1/accounts/integrations/shopify_controller.rb` | コントローラー | Shopify統合API |
| notion_controller.rb | `app/controllers/api/v1/accounts/integrations/notion_controller.rb` | コントローラー | Notion統合API |
