# 通知設計書 110-github_gists_import_errors_email

## 概要

本ドキュメントは、GitLabのGitHub Gistsインポート機能において、インポートがエラー付きで完了した際にユーザーに送信される通知メールの設計を記載するものです。

### 本通知の処理概要

本通知は、GitHub GistsからGitLab Snippetsへのインポート処理が完了したが、一部のGistがエラーによりインポートできなかった場合に、その詳細をユーザーに通知するメールです。

**業務上の目的・背景**：GitLabはGitHubからの移行を支援するため、GitHub GistsをGitLab Snippetsとしてインポートする機能を提供しています。インポート処理は非同期で行われるため、ユーザーは処理完了を待つ必要があります。処理中にエラーが発生した場合、どのGistがインポートできなかったかをユーザーに通知することで、手動での対応（再試行や手動インポート）を促すことができます。

**通知の送信タイミング**：`Gitlab::GithubGistsImport::FinishImportWorker`においてインポート処理が完了し、エラーが記録されている場合に同期送信されます（`deliver_now`）。

**通知の受信者**：インポートを開始したユーザーです。ユーザーの`notification_email_or_default`メソッドで取得されるメールアドレスに送信されます。

**通知内容の概要**：インポートが完了したことの報告、エラーによりインポートできなかったGistの一覧（GistのIDとエラー内容）、ファイル数制限に関するドキュメントへのリンク、手動移行の案内が含まれます。

**期待されるアクション**：受信者はエラー内容を確認し、必要に応じて該当のGistを手動でSnippetとして作成するか、エラー原因を解消して再度インポートを試みることが期待されます。特に10ファイル以上のGistは手動移行が必要です。

## 通知種別

メール通知

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（deliver_now） |
| 優先度 | 中 |
| リトライ | なし（同期送信のため） |

### 送信先決定ロジック

インポートを開始したユーザーのIDに基づき、`User.find(user_id)`でユーザーを取得し、`notification_email_or_default`メソッドでメールアドレスを決定します。

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | GitLabのデフォルトメールアドレス |
| 送信元名称 | GitLab |
| 件名 | `GitHub Gists import finished with errors` |
| 形式 | HTML/テキスト（マルチパート） |

### 本文テンプレート

#### HTML版（github_gists_import_errors_email.html.haml）
```
Your import of GitHub gists into GitLab snippets is complete.

GitHub gists that were not imported:

1. Gist with id {gist_id} failed due to error: {error}.
   [ファイル数制限エラーの場合: Please follow Import GitHub gists into GitLab snippets for more details.]

2. Gist with id {gist_id} failed due to error: {error}.
   ...

GitHub gists with more than 10 files must be manually migrated.
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知には添付ファイルはありません |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @errors | エラー一覧（Hash: gist_id => error） | 引数として渡される | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| バッチ/Worker | FinishImportWorker.perform | errors.present? | インポート完了時にエラーがある場合 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| errors.blank? | エラーがない場合は送信されない（正常完了） |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[GitHub Gistsインポート開始] --> B[各Gistのインポート処理]
    B --> C{エラー発生?}
    C -->|Yes| D[エラーをRedisキャッシュに記録]
    C -->|No| E[次のGistへ]
    D --> E
    E --> F{全Gist処理完了?}
    F -->|No| B
    F -->|Yes| G[FinishImportWorker.perform]
    G --> H[JobWaiter.wait]
    H --> I{全ジョブ完了?}
    I -->|No| J[再スケジュール]
    J --> G
    I -->|Yes| K[Status.finish!]
    K --> L[send_email_if_errors]
    L --> M[Redisからエラー取得]
    M --> N{errors.present?}
    N -->|No| O[終了]
    N -->|Yes| P[Notify.github_gists_import_errors_email]
    P --> Q[deliver_now]
    Q --> R[Redisキャッシュクリア]
    R --> O
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| users | ユーザー情報の取得 | |
| emails | 通知用メールアドレスの取得 | notification_email設定時 |

### テーブル別参照項目詳細

#### users

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | ユーザーID | WHERE id = user_id |
| email | 登録メールアドレス | |
| notification_email | 通知用メールアドレス | |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | エラー情報はRedisに保存される |

### Redis参照

| キー | 用途 | 備考 |
|-----|------|------|
| gitlab:github-gists-import:{user_id}:errors | エラー情報のハッシュ | Gist ID => エラーメッセージ |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ユーザー未検出 | User.find(user_id)が例外 | 例外がスローされる |
| 送信失敗 | SMTP接続エラー等 | Worker側でエラーハンドリング |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（同期送信のため） |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | GitLab全体のメール送信レート制限に準拠 |
| 1日あたり上限 | GitLab全体のメール送信レート制限に準拠 |

### 配信時間帯

特に制限なし（インポート完了時に即時送信）

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

- GitHub Gist IDが通知に含まれるが、これは公開情報
- エラーメッセージにはセンシティブな情報は含まれない
- インポートを実行したユーザー本人にのみ送信される

## 備考

- GitLab Snippetsは最大10ファイルまでの制限があるため、10ファイル以上のGistは自動インポートできない
- `FILE_COUNT_LIMIT_MESSAGE`エラーの場合、ドキュメントへのリンクが追加表示される
- エラー情報はRedisに一時保存され、メール送信後にキャッシュがクリアされる

---

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

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

### 推奨読解順序

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

エラー情報の保存・取得方法を理解します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | caching.rb | `lib/gitlab/cache/import/caching.rb` | values_from_hash、expire |

**読解のコツ**: Redisハッシュを使用したエラー情報の一時保存の仕組みを理解する。

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

Workerでの通知送信処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | finish_import_worker.rb | `app/workers/gitlab/github_gists_import/finish_import_worker.rb` | perform、send_email_if_errors |

**主要処理フロー**:
1. **23-32行目**: perform - ジョブ完了待機とステータス更新
2. **29行目**: send_email_if_errors呼び出し
3. **46-55行目**: send_email_if_errors - エラー取得とメール送信

#### Step 3: メーラーを理解する

通知メールの生成処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | imports.rb | `app/mailers/emails/imports.rb` | github_gists_import_errors_emailメソッド |

**主要処理フロー**:
- **5-13行目**: github_gists_import_errors_email
- **6行目**: @errorsの設定
- **7行目**: User.find(user_id)
- **9-12行目**: email_with_layout

#### Step 4: ビューテンプレートを理解する

メール本文のテンプレートを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | github_gists_import_errors_email.html.haml | `app/views/notify/github_gists_import_errors_email.html.haml` | エラー一覧の表示、ファイル数制限の説明 |
| 4-2 | github_gists_import_errors_email.text.erb | `app/views/notify/github_gists_import_errors_email.text.erb` | テキスト形式の本文テンプレート |

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

```
Gitlab::GithubGistsImport::FinishImportWorker#perform(user_id, waiter_key, remaining)
    │
    ├─ wait_for_jobs(waiter_key, remaining)
    │      └─ JobWaiter.new(remaining, key).wait
    │
    ├─ Gitlab::GithubGistsImport::Status.new(user_id).finish!
    │
    └─ send_email_if_errors(user_id)
           │
           ├─ key = format(GISTS_ERRORS_BY_ID, user_id: user_id)
           │
           ├─ Gitlab::Cache::Import::Caching.values_from_hash(key)
           │
           └─ (errors.present? の場合)
                  │
                  ├─ Notify.github_gists_import_errors_email(user_id, errors)
                  │      │
                  │      ├─ @errors = errors
                  │      │
                  │      ├─ User.find(user_id)
                  │      │
                  │      └─ email_with_layout
                  │             └─ deliver_now
                  │
                  └─ Gitlab::Cache::Import::Caching.expire(key, ...)
```

### データフロー図

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

GitHub Gist              GistImporter                      GitLab Snippet
インポートリクエスト ───▶  （各Gist処理）              ───▶ （成功時）
                                   │
                                   │（エラー時）
                                   ▼
                         Redisにエラー記録
                                   │
                                   ▼
                         FinishImportWorker          ───▶  エラー通知メール送信
                         #send_email_if_errors              （ユーザーへ）
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| finish_import_worker.rb | `app/workers/gitlab/github_gists_import/finish_import_worker.rb` | ソース | インポート完了ワーカー |
| imports.rb | `app/mailers/emails/imports.rb` | ソース | インポート関連メーラー |
| github_gists_import_errors_email.html.haml | `app/views/notify/github_gists_import_errors_email.html.haml` | テンプレート | HTML本文テンプレート |
| github_gists_import_errors_email.text.erb | `app/views/notify/github_gists_import_errors_email.text.erb` | テンプレート | テキスト本文テンプレート |
| gist_importer.rb | `lib/gitlab/github_gists_import/importer/gist_importer.rb` | ソース | Gistインポート処理 |
| caching.rb | `lib/gitlab/cache/import/caching.rb` | ソース | Redisキャッシュ操作 |
