# 機能設計書 150-オートコンプリート

## 概要

本ドキュメントは、GitLabのオートコンプリート機能に関する設計書である。オートコンプリート機能はユーザー、イシュー、マージリクエスト、ラベル、マイルストーン等の候補をリアルタイムで取得し、入力補完を提供する。

### 本機能の処理概要

オートコンプリート機能は、GitLabの各種入力フィールドで候補を提供するためのAPIエンドポイント群である。@メンション、#イシュー参照、!MR参照など、GitLab Flavored Markdownでの参照入力時に候補を表示し、ユーザーの入力を補助する。

**業務上の目的・背景**：イシューやマージリクエストのコメント、説明文などでユーザーメンションやリソース参照を行う際に、正確な入力を支援する。手動でのID入力ミスを防ぎ、作業効率を向上させる。

**機能の利用シーン**：
- コメント入力時の@ユーザーメンション候補表示
- イシュー参照（#123）入力時の候補表示
- マージリクエスト参照（!456）入力時の候補表示
- ラベル参照（~label）入力時の候補表示
- マイルストーン参照（%milestone）入力時の候補表示
- クイックアクションコマンド（/command）入力時の候補表示
- スニペット参照入力時の候補表示
- CRM連絡先参照入力時の候補表示
- Wiki参照入力時の候補表示

**主要な処理内容**：
1. プロジェクトメンバー一覧の取得
2. イシュー一覧の取得
3. マージリクエスト一覧の取得
4. ラベル一覧の取得
5. マイルストーン一覧の取得
6. クイックアクションコマンド一覧の取得
7. スニペット一覧の取得
8. CRM連絡先一覧の取得
9. Wikiページ一覧の取得

**関連システム・外部連携**：
- Wikiシステム（Wikiページ参照）
- CRMシステム（連絡先参照）

**権限による制御**：
- マイルストーン: read_milestone権限が必要
- CRM連絡先: read_crm_contact権限が必要
- その他: プロジェクトへの読み取り権限

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 各種入力フォーム | 補助機能 | オートコンプリート候補の取得 |

## 機能種別

データ取得 / APIエンドポイント

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | String | Yes（一部） | ターゲットタイプ（Issue, MergeRequest等） | 有効なタイプ |
| type_id | Integer | No | ターゲットのIID | 存在するIID |
| search | String | No | 検索文字列（イシュー等） | 文字列 |

### 入力データソース

- URLパラメータ
- 各種テーブル（issues, merge_requests, labels, milestones等）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 候補リスト | Array | オートコンプリート候補の配列 |

**候補オブジェクト例（イシュー）**:
```json
[
  {
    "iid": 123,
    "title": "Fix bug in login",
    "icon_name": "issue-type-issue"
  }
]
```

**候補オブジェクト例（メンバー）**:
```json
[
  {
    "username": "john_doe",
    "name": "John Doe",
    "avatar_url": "..."
  }
]
```

### 出力先

- JSON形式HTTPレスポンス

## 処理フロー

### 処理シーケンス

```
1. オートコンプリートリクエスト受信
   └─ エンドポイント別に処理を分岐

2. 権限確認（必要な場合）
   └─ authorize_read_milestone!（milestones）
   └─ authorize_read_crm_contact!（contacts）

3. 候補データ取得
   └─ AutocompleteService経由で取得
   └─ 検索条件がある場合はフィルタリング
   └─ 件数制限（SEARCH_LIMIT = 5）

4. JSON形式でレスポンス
```

### フローチャート

```mermaid
flowchart TD
    A[オートコンプリートリクエスト] --> B{エンドポイント判定}

    B -->|members| C[ParticipantsService]
    B -->|issues| D[AutocompleteService.issues]
    B -->|merge_requests| E[AutocompleteService.merge_requests]
    B -->|labels| F[AutocompleteService.labels_as_hash]
    B -->|milestones| G{権限確認}
    B -->|commands| H[AutocompleteService.commands]
    B -->|snippets| I[AutocompleteService.snippets]
    B -->|contacts| J{権限確認}
    B -->|wikis| K[AutocompleteService.wikis]

    G -->|OK| L[AutocompleteService.milestones]
    G -->|NG| M[404エラー]

    J -->|OK| N[AutocompleteService.contacts]
    J -->|NG| M

    C --> O[JSON レスポンス]
    D --> O
    E --> O
    F --> O
    L --> O
    H --> O
    I --> O
    N --> O
    K --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-150-01 | 検索件数制限 | 検索結果は最大5件まで | issuesアクション |
| BR-150-02 | マイルストーン権限 | read_milestone権限が必要 | milestonesアクション |
| BR-150-03 | CRM連絡先権限 | read_crm_contact権限が必要 | contactsアクション |
| BR-150-04 | オープン状態フィルタ | イシュー/MRはオープン状態のみ取得 | issues, merge_requestsアクション |
| BR-150-05 | Wiki除外パス | templates/, uploads/で始まるページは除外 | wikisアクション |
| BR-150-06 | 組織ID付与 | 現在の組織IDをパラメータに追加 | 全アクション |

### 計算ロジック

**イシュー検索**:
```ruby
relation = IssuesFinder.new(current_user, project_id: project.id, state: 'opened').execute
relation = relation.gfm_autocomplete_search(params[:search]).limit(SEARCH_LIMIT) if params[:search]
```

**マイルストーン取得**:
```ruby
finder_params = {
  project_ids: [@project.id],
  state: :active,
  order: { due_date: :asc, title: :asc }
}
finder_params[:group_ids] = @project.group.self_and_ancestors.select(:id) if @project.group
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| イシュー取得 | issues | SELECT | オープンイシュー検索 |
| MR取得 | merge_requests | SELECT | オープンMR検索 |
| ラベル取得 | labels | SELECT | ラベル一覧取得 |
| マイルストーン取得 | milestones | SELECT | アクティブマイルストーン取得 |
| スニペット取得 | snippets | SELECT | プロジェクトスニペット取得 |
| 連絡先取得 | customer_relations_contacts | SELECT | CRM連絡先取得 |

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

#### issues

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | iid, title, namespace_id, icon_name | project_id = :id AND state = 'opened' | オープンイシュー |

#### milestones

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | iid, title, due_date | project_id = :id AND state = 'active' | アクティブマイルストーン |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | Not Found | read_milestone権限なし | render_404 |
| 404 | Not Found | read_crm_contact権限なし | render_404 |

### リトライ仕様

特になし

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

読み取り専用のためトランザクション管理不要

## パフォーマンス要件

- urgency: low（全エンドポイント）
- ExpiresIn concernによるキャッシュヘッダ設定
- 検索結果は最大5件に制限

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

- エンドポイント別の権限チェック
- CRM連絡先はグループ権限で制御
- 組織IDのスコープ制御

## 備考

- AutocompleteService経由でデータ取得
- feature_category: team_planning, wiki, code_review_workflow, groups_and_projects, source_code_management
- QuickActions::TargetServiceでターゲットオブジェクト取得
- prepend_mod_withでEE機能拡張可能

---

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

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

### 推奨読解順序

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

メインコントローラの処理フローを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | autocomplete_sources_controller.rb | `app/controllers/projects/autocomplete_sources_controller.rb` | オートコンプリートのエントリーポイント |

**主要処理フロー**:
1. **6行目**: authorize_read_milestone! - マイルストーン権限確認
2. **7行目**: authorize_read_crm_contact! - CRM連絡先権限確認
3. **9-16行目**: feature_category設定
4. **18-19行目**: members アクション - ParticipantsService使用
5. **22-23行目**: issues アクション
6. **26-27行目**: merge_requests アクション
7. **30-31行目**: labels アクション
8. **34-35行目**: milestones アクション
9. **38-39行目**: commands アクション
10. **42-43行目**: snippets アクション
11. **46-47行目**: contacts アクション
12. **50-51行目**: wikis アクション
13. **56-62行目**: autocomplete_service - サービスインスタンス生成

**読解のコツ**:
- 各アクションはautocomplete_serviceの対応するメソッドを呼び出す
- Current.organization.idが組織IDとして渡される

#### Step 2: サービス実装を理解する

AutocompleteServiceの詳細を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | autocomplete_service.rb | `app/services/projects/autocomplete_service.rb` | オートコンプリートサービス |

**主要処理フロー**:
1. **8行目**: SEARCH_LIMIT = 5 - 検索結果制限
2. **10-16行目**: issues - IssuesFinderでオープンイシュー取得
3. **18-27行目**: milestones - MilestonesFinderでアクティブマイルストーン取得
4. **30-31行目**: merge_requests - MergeRequestsFinderでオープンMR取得
5. **34-37行目**: commands - QuickActions::InterpretService使用
6. **40-45行目**: snippets - SnippetsFinder使用
7. **48-56行目**: wikis - Wikiページ取得（templates/, uploads/除外）
8. **58-73行目**: contacts - CRM連絡先取得

#### Step 3: ターゲット取得を理解する

QuickActions::TargetServiceの詳細を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | target_service.rb | `app/services/quick_actions/target_service.rb` | ターゲットサービス |

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

```
Projects::AutocompleteSourcesController
    │
    ├─ members
    │      └─ Projects::ParticipantsService.new(@project, current_user, params).execute(target)
    │
    ├─ issues
    │      └─ autocomplete_service.issues
    │             └─ IssuesFinder.new(...).execute
    │
    ├─ merge_requests
    │      └─ autocomplete_service.merge_requests
    │             └─ MergeRequestsFinder.new(...).execute
    │
    ├─ labels
    │      └─ autocomplete_service.labels_as_hash(target)
    │
    ├─ milestones
    │      └─ autocomplete_service.milestones
    │             └─ MilestonesFinder.new(...).execute
    │
    ├─ commands
    │      └─ autocomplete_service.commands(target)
    │             └─ QuickActions::InterpretService.new(...).available_commands
    │
    ├─ snippets
    │      └─ autocomplete_service.snippets
    │             └─ SnippetsFinder.new(...).execute
    │
    ├─ contacts
    │      └─ autocomplete_service.contacts(target)
    │             └─ Crm::ContactsFinder.new(...).execute
    │
    └─ wikis
           └─ autocomplete_service.wikis
                  └─ Wiki.for_container(project, current_user).list_pages
```

### データフロー図

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

GET /autocomplete/       ───▶  AutocompleteSourcesController ───▶  JSON (candidates)
issues?search=xxx              └─ AutocompleteService.issues
                                     └─ IssuesFinder
                                           └─ gfm_autocomplete_search

GET /autocomplete/       ───▶  AutocompleteSourcesController ───▶  JSON (candidates)
members                        └─ ParticipantsService
                                     └─ メンバー情報取得
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| autocomplete_sources_controller.rb | `app/controllers/projects/autocomplete_sources_controller.rb` | コントローラ | オートコンプリートエントリーポイント |
| autocomplete_service.rb | `app/services/projects/autocomplete_service.rb` | サービス | オートコンプリートサービス |
| participants_service.rb | `app/services/projects/participants_service.rb` | サービス | 参加者サービス |
| target_service.rb | `app/services/quick_actions/target_service.rb` | サービス | ターゲットサービス |
| issues_finder.rb | `app/finders/issues_finder.rb` | ファインダー | イシュー検索 |
| merge_requests_finder.rb | `app/finders/merge_requests_finder.rb` | ファインダー | MR検索 |
| milestones_finder.rb | `app/finders/milestones_finder.rb` | ファインダー | マイルストーン検索 |
| snippets_finder.rb | `app/finders/snippets_finder.rb` | ファインダー | スニペット検索 |
| contacts_finder.rb | `app/finders/crm/contacts_finder.rb` | ファインダー | CRM連絡先検索 |
| expires_in.rb | `app/controllers/concerns/autocomplete_sources/expires_in.rb` | Concern | キャッシュヘッダ設定 |
