# 画面設計書 281-スニペット編集

## 概要

本ドキュメントは、GitLabの個人スニペット編集画面の設計を記載したものである。

### 本画面の処理概要

**業務上の目的・背景**：スニペットは、コードの断片やテキストを保存・共有するための機能である。個人スニペット編集画面は、ユーザーが作成済みのスニペットの内容（タイトル、説明、ファイル内容、公開範囲など）を変更するために必要な画面である。開発者がコード断片を最新の状態に保つ、誤りを修正する、公開範囲を変更するといった日常的な業務ニーズに対応する。

**画面へのアクセス方法**：ユーザーはスニペット詳細画面から「Edit」ボタンをクリックするか、URLで直接 `/-/snippets/:id/edit` にアクセスすることでこの画面に到達する。編集権限を持つスニペット作成者のみがアクセス可能である。

**主要な操作・処理内容**：
1. スニペットのタイトルを編集する
2. スニペットの説明（Markdown形式）を編集する
3. ファイル名を変更する
4. ファイルの内容をMonacoエディタで編集する
5. 公開範囲（Private/Internal/Public）を変更する
6. 変更を保存してスニペットを更新する
7. 編集をキャンセルしてスニペット詳細画面に戻る

**画面遷移**：
- 遷移元：スニペット詳細画面 (`/-/snippets/:id`)、ダッシュボードスニペット一覧
- 遷移先：スニペット詳細画面（保存成功時）、スニペット詳細画面（キャンセル時）

**権限による表示制御**：スニペットの作成者（author）のみが編集画面にアクセス可能。他のユーザーがアクセスしようとすると403エラーが返される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 72 | 個人スニペット | 主機能 | 個人スニペットの編集処理 |

## 画面種別

編集

## URL/ルーティング

| メソッド | パス | コントローラー#アクション |
|----------|------|---------------------------|
| GET | `/-/snippets/:id/edit` | `snippets#edit` |
| PATCH/PUT | `/-/snippets/:id` | GraphQL Mutation: `updateSnippet` |

## 入出力項目

| 項目名 | 項目ID | データ型 | 必須 | 最大長 | 説明 |
|--------|--------|----------|------|--------|------|
| タイトル | title | String | Yes | 255文字 | スニペットのタイトル |
| 説明 | description | String | No | 1MB | スニペットの説明（Markdown形式） |
| ファイル名 | file_name | String | No | 255文字 | スニペットファイルの名前 |
| ファイル内容 | content | String | Yes | 設定による | スニペットの本文コンテンツ |
| 公開範囲 | visibility_level | Integer | Yes | - | 0:Private, 10:Internal, 20:Public |

## 表示項目

| 項目名 | 説明 |
|--------|------|
| ページタイトル | 「Edit snippet」と表示 |
| パンくずリスト | Snippets > スニペット参照 > Edit |
| スニペットフォーム | Vue.jsコンポーネントで構成された編集フォーム |

## イベント仕様

### 1-保存ボタン押下

**トリガー**：「Save changes」ボタンをクリック

**処理フロー**：
1. フォームバリデーションを実行
2. GraphQL Mutation `updateSnippet` を呼び出し
3. 成功時：スニペット詳細画面へリダイレクト
4. 失敗時：エラーメッセージを表示

**データフロー**：
```
ユーザー入力 -> Vue.jsコンポーネント -> GraphQL API -> SnippetService -> Snippet Model -> Database
```

### 2-キャンセルボタン押下

**トリガー**：「Cancel」ボタンまたはリンクをクリック

**処理フロー**：
1. 変更を破棄
2. スニペット詳細画面へ遷移

### 3-Markdownプレビュー

**トリガー**：説明欄でプレビュータブをクリック

**処理フロー**：
1. `/preview_markdown` エンドポイントにPOSTリクエスト
2. レンダリングされたHTMLを取得して表示

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 保存ボタン押下 | snippets | UPDATE | スニペットレコードの更新 |
| 保存ボタン押下 | snippet_repositories | UPDATE | リポジトリ情報の更新（必要時） |

### テーブル別更新項目詳細

#### snippets

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | title | フォーム入力値 | |
| UPDATE | description | フォーム入力値 | |
| UPDATE | file_name | フォーム入力値 | |
| UPDATE | content | フォーム入力値 | |
| UPDATE | visibility_level | フォーム入力値 | |
| UPDATE | updated_at | 現在時刻 | 自動設定 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG001 | 成功 | Snippet was successfully updated. | スニペット更新成功時 |
| MSG002 | エラー | Title can't be blank | タイトル未入力時 |
| MSG003 | エラー | Content can't be blank | 本文未入力時 |
| MSG004 | エラー | Title is too long (maximum is 255 characters) | タイトル超過時 |
| MSG005 | エラー | You are not authorized to perform this action | 権限不足時 |

## 例外処理

| 例外条件 | 画面表示・動作 |
|----------|---------------|
| スニペットが存在しない | 404 Not Foundページを表示 |
| 編集権限がない | 403 Forbiddenエラーを返却 |
| バリデーションエラー | フォーム上にエラーメッセージを表示 |
| サーバーエラー | 500エラーページを表示 |
| スパム検出 | CAPTCHA認証を要求（パブリック変更時） |

## 備考

- Monacoエディタを使用してシンタックスハイライト付きでコード編集が可能
- ファイル拡張子に基づいてシンタックスハイライトが自動適用される
- 最大ファイル数は10ファイルまで
- コンテンツサイズ制限は `Gitlab::CurrentSettings.snippet_size_limit` で設定される

---

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

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

### 推奨読解順序

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

まず、スニペットのデータモデルを理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | snippet.rb | `app/models/snippet.rb` | スニペットの属性、バリデーション、リレーションを理解する |

**読解のコツ**: `validates` メソッドでバリデーションルールを、`belongs_to` と `has_many` でリレーションを確認する。

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

ビューとコントローラーの流れを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | edit.html.haml | `app/views/snippets/edit.html.haml` | 編集画面のテンプレート構造 |
| 2-2 | snippets_controller.rb | `app/controllers/snippets_controller.rb` | editアクションの処理 |
| 2-3 | snippets_actions.rb | `app/controllers/concerns/snippets_actions.rb` | 共通アクションの定義 |

**主要処理フロー**:
1. **行9**: `before_action :snippet` でスニペットをロード
2. **行13**: `authorize_update_snippet!` で編集権限をチェック
3. **行23**: `edit` アクションは空（ビューレンダリングのみ）

#### Step 3: フロントエンドを理解する

Vue.jsコンポーネントによるフォーム実装を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _form.html.haml | `app/views/shared/snippets/_form.html.haml` | Vue.jsマウントポイントの定義 |
| 3-2 | snippet_edit.vue | `app/assets/javascripts/snippets/components/` | 編集フォームのVue.jsコンポーネント |

#### Step 4: GraphQL APIを理解する

更新処理はGraphQL経由で実行される。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | update_snippet_mutation.rb | `app/graphql/mutations/snippets/` | GraphQL Mutationの定義 |
| 4-2 | update_service.rb | `app/services/snippets/update_service.rb` | 更新ビジネスロジック |

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

```
snippets#edit (Controller)
    │
    ├─ before_action :snippet (SnippetsActions)
    │      └─ Snippet.find_by_id
    │
    ├─ authorize_update_snippet! (Authorization)
    │
    └─ render 'edit'
           │
           └─ shared/snippets/_form (Vue.js mount)
                  │
                  └─ GraphQL: updateSnippet
                         │
                         └─ Snippets::UpdateService
                                │
                                └─ Snippet#update
```

### データフロー図

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

フォーム入力データ ───▶ Vue.jsコンポーネント ───▶ GraphQL API
                                │
                                ▼
                         UpdateService
                                │
                                ▼
                         Snippet Model
                                │
                                ▼
                         PostgreSQL DB ───▶ スニペット詳細画面
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| edit.html.haml | `app/views/snippets/edit.html.haml` | テンプレート | 編集画面のビュー |
| _form.html.haml | `app/views/shared/snippets/_form.html.haml` | テンプレート | スニペットフォームパーシャル |
| snippets_controller.rb | `app/controllers/snippets_controller.rb` | コントローラー | スニペットコントローラー |
| snippets_actions.rb | `app/controllers/concerns/snippets_actions.rb` | Concern | 共通アクション定義 |
| snippet.rb | `app/models/snippet.rb` | モデル | スニペットモデル |
| update_service.rb | `app/services/snippets/update_service.rb` | サービス | 更新ビジネスロジック |
| routes/snippets.rb | `config/routes/snippets.rb` | 設定 | ルーティング定義 |
