# 画面設計書 139-スニペット新規作成

## 概要

本ドキュメントは、GitLabにおけるプロジェクトスニペット新規作成画面の設計を定義するものである。

### 本画面の処理概要

**業務上の目的・背景**：プロジェクトに関連するコードスニペット、設定ファイル、メモなどを新規作成し、チームで共有するための画面である。スニペットは複数のファイルを含むことができ、構文ハイライト付きでコードを保存・表示できる。これにより、プロジェクト固有のサンプルコード、設定テンプレート、ドキュメント断片などを効率的に管理・共有できる。

**画面へのアクセス方法**：スニペット一覧画面から「New snippet」ボタンをクリック、またはプロジェクトメニューから直接アクセスできる。URLパスは `/:namespace/:project/-/snippets/new` となる。

**主要な操作・処理内容**：
1. スニペットのタイトルを入力
2. スニペットの説明（オプション、Markdown対応）を入力
3. ファイル名とコンテンツを入力
4. 可視性レベル（公開/内部/非公開）を選択
5. 追加ファイルを追加（オプション、最大10ファイル）
6. スニペットを作成

**画面遷移**：
- 遷移元：スニペット一覧画面、プロジェクトメニュー
- 遷移先：スニペット詳細画面（作成成功時）、同一画面（エラー時）

**権限による表示制御**：プロジェクトのスニペット作成権限（`create_snippet`）を持つユーザーのみがこの画面にアクセスできる。可視性レベルの選択肢は、ユーザーの権限とシステム設定に応じて制限される場合がある。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 71 | プロジェクトスニペット | 主機能 | 新規スニペットの作成 |

## 画面種別

登録

## URL/ルーティング

| メソッド | パス | コントローラ#アクション |
|---------|------|------------------------|
| GET | `/:namespace_id/:project_id/-/snippets/new` | `projects/snippets#new` |

スニペット作成はGraphQL Mutationで行われる。

## 入出力項目

| 項目名 | 項目ID | 型 | 必須 | 入出力 | バリデーション | 説明 |
|--------|--------|-----|------|--------|---------------|------|
| タイトル | title | String | Yes | 入力 | 最大255文字 | スニペットのタイトル |
| 説明 | description | String | No | 入力 | 最大1MB | Markdown対応の説明文 |
| ファイル名 | file_name | String | No | 入力 | 最大255文字 | スニペットのファイル名 |
| コンテンツ | content | Text | Yes | 入力 | サイズ制限あり | スニペットの内容 |
| 可視性レベル | visibility_level | Integer | Yes | 入力 | 0/10/20 | 0=非公開,10=内部,20=公開 |
| 追加ファイル | files | Array | No | 入力 | 最大10ファイル | 複数ファイル対応 |

## 表示項目

この画面はVue.jsコンポーネント（`#js-snippet-edit`）によりレンダリングされる。

| 項目名 | 項目ID | 型 | 説明 |
|--------|--------|-----|------|
| パンくずリスト | breadcrumbs | Component | Snippets > New snippet |
| ページタイトル | page_title | String | 「New snippet」 |
| ページ見出し | heading | Component | 「New snippet」（PageHeadingComponent） |
| スニペットフォーム | snippet_form | Vue Component | 入力フォーム |

### Vueコンポーネントへ渡すデータ

| 項目名 | 説明 |
|--------|------|
| project_path | プロジェクトのフルパス |
| snippet-gid | 空文字（新規作成時） |
| markdown-preview-path | Markdownプレビュー用APIパス |
| markdown-docs-path | Markdownドキュメントへのパス |
| visibility_levels | 選択可能な可視性レベル（JSON） |
| selected_level | デフォルト可視性レベル |
| multiple_levels_restricted | 複数レベル制限フラグ |

## イベント仕様

### 1-スニペット作成

**トリガー**：Vueコンポーネント内の「Create snippet」ボタンクリック

**処理フロー**：
1. フロントエンドでフォームのバリデーション実行
2. GraphQL Mutation `snippetCreate` を呼び出し
3. バックエンドでスニペット作成処理を実行
4. スニペットリポジトリを作成（初回時）
5. ファイルをコミット
6. 作成成功時、スニペット詳細画面にリダイレクト
7. 作成失敗時、エラーメッセージを表示

### 2-ファイル追加

**トリガー**：「Add file」ボタンクリック

**処理フロー**：
1. 新しいファイル入力フィールドを追加
2. 最大10ファイルまで追加可能

### 3-Markdownプレビュー

**トリガー**：説明フィールドの「Preview」タブクリック

**処理フロー**：
1. markdown-preview-path APIを呼び出し
2. Markdownをレンダリング
3. プレビュー表示

### 4-キャンセル

**トリガー**：「Cancel」ボタンまたはパンくずリストクリック

**処理フロー**：
1. スニペット一覧画面へ遷移

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| スニペット作成 | snippets | INSERT | スニペットレコード作成 |
| スニペット作成 | snippet_repositories | INSERT | リポジトリ情報作成 |
| スニペット作成 | snippet_statistics | INSERT | 統計情報作成 |

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

#### snippets

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | 自動採番 | Primary Key |
| INSERT | title | 入力値 | タイトル |
| INSERT | description | 入力値 | 説明（Markdown） |
| INSERT | content | 入力値 | コンテンツ |
| INSERT | file_name | 入力値 | ファイル名 |
| INSERT | visibility_level | 入力値 | 0/10/20 |
| INSERT | author_id | current_user.id | 作成者 |
| INSERT | project_id | 対象プロジェクトID | FK |
| INSERT | organization_id | NULL | プロジェクト所属の場合はNULL |
| INSERT | created_at | 現在時刻 | 作成日時 |
| INSERT | updated_at | 現在時刻 | 更新日時 |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG001 | エラー | Title is required | タイトル未入力時 |
| MSG002 | エラー | Content is required | コンテンツ未入力時 |
| MSG003 | エラー | Content is too long (maximum is X bytes) | コンテンツサイズ超過時 |
| MSG004 | エラー | Description is too long (maximum is 1 MB) | 説明サイズ超過時 |
| MSG005 | エラー | File name is too long (maximum is 255 characters) | ファイル名超過時 |
| MSG006 | エラー | Maximum 10 files allowed | ファイル数超過時 |

## 例外処理

| 例外条件 | 処理内容 | 遷移先 |
|---------|---------|--------|
| プロジェクト未検出 | 404 Not Found | Not Found画面 |
| 権限不足 | 404 Not Found | Not Found画面 |
| スニペット機能無効 | 404 Not Found | Not Found画面 |
| バリデーションエラー | エラーメッセージ表示 | 同一画面 |
| スパム検出 | キャプチャ表示 | 同一画面（reCAPTCHA） |

## 備考

- この画面はVue.jsコンポーネント（`#js-snippet-edit`）によりレンダリングされる
- 新規作成時は `snippet-gid` が空文字
- 可視性レベルはシステム設定とプロジェクト設定に依存
- デフォルト可視性レベルは `snippets_selected_visibility_level` ヘルパーで決定
- スニペットはGitリポジトリとして保存される（複数ファイル対応）
- 最大ファイル数は `Snippet::MAX_FILE_COUNT`（10）で制限
- コンテンツサイズは `Gitlab::CurrentSettings.snippet_size_limit` で制限
- スパム検知が有効な場合、reCAPTCHAが表示される場合がある

---

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

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

### 推奨読解順序

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

Snippetモデルとバリデーションを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | snippet.rb | `app/models/snippet.rb` | Snippetモデル、バリデーション、定数定義 |

**読解のコツ**: `MAX_FILE_COUNT = 10`、`DESCRIPTION_LENGTH_MAX = 1.megabyte`、バリデーション（title、content、file_name）を確認。`visibility_level` の初期化ロジックも重要。

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

ビューテンプレートとコントローラの処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | new.html.haml | `app/views/projects/snippets/new.html.haml` | スニペット作成画面のビュー |
| 2-2 | snippets_controller.rb | `app/controllers/projects/snippets_controller.rb` | newアクション |
| 2-3 | _form.html.haml | `app/views/shared/snippets/_form.html.haml` | スニペットフォーム（共有パーシャル） |

**主要処理フロー**:
1. **行13**: `authorize_create_snippet!` で作成権限チェック（before_action）
2. **行36-38**: `new` アクションで空のスニペットオブジェクトを初期化
3. パーシャルでVueコンポーネントをマウント

#### Step 3: 共有フォームを理解する

スニペットフォームの共有コンポーネントを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | _form.html.haml | `app/views/shared/snippets/_form.html.haml` | Vueコンポーネントマウントポイント |
| 3-2 | snippets_helper.rb | `app/helpers/snippets_helper.rb` | 可視性レベル関連ヘルパー |

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

スニペット作成のGraphQL処理を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | create.rb | `app/graphql/mutations/snippets/create.rb` | GraphQL Mutation定義 |
| 4-2 | create_service.rb | `app/services/snippets/create_service.rb` | 作成サービス |

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

```
projects/snippets/new.html.haml
    │
    ├─ パンくずリスト（Snippets > New snippet）
    │
    ├─ Layouts::PageHeadingComponent（見出し）
    │
    └─ shared/snippets/_form.html.haml
           │
           └─ #js-snippet-edit (Vue Component)
                  │
                  ├─ data: project_path
                  ├─ data: snippet-gid (空文字)
                  ├─ data: markdown-preview-path
                  ├─ data: visibility_levels
                  └─ data: selected_level
                         │
                         └─ Vue Form Component
                                │
                                ├─ Title入力
                                ├─ Description入力（Markdown）
                                ├─ File入力（複数可）
                                └─ Visibility選択
                                       │
                                       └─ GraphQL Mutation (snippetCreate)
                                              │
                                              └─ Snippets::CreateService
```

### データフロー図

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

                    SnippetsController#new
                           │
                           ▼
                    @snippet = project.snippets.build
                           │
                           ▼
                    _form.html.haml render
                           │
                           ▼
                    #js-snippet-edit マウント

Form Data ──────────────────────────────────▶ GraphQL Mutation
(title,                                             │
 description,                                       ▼
 file_name,                              Snippets::CreateService
 content,                                           │
 visibility_level)                                  ├─ バリデーション
                                                    ├─ スパムチェック
                                                    ├─ Snippet作成
                                                    └─ Repository作成
                                                           │
                                                           ▼
                                               スニペット詳細画面へ
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| new.html.haml | `app/views/projects/snippets/new.html.haml` | テンプレート | スニペット作成画面 |
| snippets_controller.rb | `app/controllers/projects/snippets_controller.rb` | コントローラ | HTTPリクエスト処理 |
| _form.html.haml | `app/views/shared/snippets/_form.html.haml` | パーシャル | スニペットフォーム |
| snippet.rb | `app/models/snippet.rb` | モデル | Snippetエンティティ |
| create.rb | `app/graphql/mutations/snippets/create.rb` | GraphQL | Mutation定義 |
| create_service.rb | `app/services/snippets/create_service.rb` | サービス | 作成ロジック |
| snippets_helper.rb | `app/helpers/snippets_helper.rb` | ヘルパー | 可視性レベル等 |
| project.rb | `config/routes/project.rb` | ルーティング | URLルーティング定義（行433-437） |
