# 画面設計書 88-リリース編集

## 概要

本ドキュメントは、GitLabのリリース編集画面の設計仕様を定義するものである。

### 本画面の処理概要

リリース編集画面は、既存のリリースを編集するための画面である。リリースノート、リリース日時、マイルストーン、アセットリンクなどを変更できる。タグ名は作成後に変更できない。

**業務上の目的・背景**：リリース後にリリースノートの修正が必要になった場合や、追加のアセットリンクを登録したい場合、マイルストーンの関連付けを変更したい場合に使用する。リリース日時の変更により「Upcoming Release」から通常リリースへの移行も可能。

**画面へのアクセス方法**：リリース詳細画面またはリリース一覧画面から編集ボタン（鉛筆アイコン）をクリック、またはURL直接アクセス。URLパターンは `/:namespace/:project/-/releases/:tag/edit` となる。

**主要な操作・処理内容**：
1. リリースタイトルの変更
2. リリースノートの編集（Markdown対応）
3. リリース日時の変更
4. マイルストーンの追加・削除
5. アセットリンクの追加・変更・削除
6. リリースの削除

**画面遷移**：
- 遷移元：リリース一覧画面、リリース詳細画面
- 遷移先：リリース一覧画面（保存成功時）、同一画面（エラー時）

**権限による表示制御**：
- `update_release`権限がない場合はアクセス不可
- `destroy_release`権限を持つユーザーのみ削除ボタンが表示される

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 73 | リリース作成 | 主機能 | リリースの編集 |
| 74 | リリースアセット | 補助機能 | リリースアセットの編集 |

## 画面種別

編集

## URL/ルーティング

```
GET /:namespace/:project/-/releases/:tag/edit
POST /api/graphql (GraphQL Mutation: releaseUpdate)
POST /api/graphql (GraphQL Mutation: releaseDelete)
```

## 入出力項目

### 入力項目

| 項目名 | データ型 | 必須 | バリデーション | 説明 |
|--------|----------|------|----------------|------|
| Tag name | String | - | 読み取り専用 | 関連付けるGitタグ名（変更不可） |
| Release title | String | - | 最大255文字 | リリース名（空の場合タグ名を使用） |
| Release notes | String | - | - | リリースノート（Markdown） |
| Released at | DateTime | - | - | リリース日時 |
| Milestones | Array<String> | - | 有効なマイルストーン | 関連マイルストーン |
| Release assets | Array<Link> | - | - | アセットリンク |

### アセットリンク項目

| 項目名 | データ型 | 必須 | 説明 |
|--------|----------|------|------|
| id | String | - | 既存リンクのID（更新時） |
| name | String | 必須 | リンク名 |
| url | String | 必須 | リンクURL |
| type | String | - | リンクタイプ（other/runbook/image/package） |
| filepath | String | - | 直接ダウンロード用パス |

## 表示項目

### ヘッダー情報

| 項目名 | データ型 | 説明 | ソース |
|--------|----------|------|--------|
| タイトル | String | "Edit Release" | 固定値 |
| パンくずリスト | Array | Releases > リリース名 > Edit | 動的生成 |

### フォーム項目

| 項目名 | データ型 | 説明 | ソース |
|--------|----------|------|--------|
| タグ名（読み取り専用） | String | 関連するGitタグ | release.tag |
| リリースタイトル | String | リリース名の入力フィールド | release.name |
| リリースノート | String | Markdownエディタ | release.description |
| リリース日時 | DateTime | 日時ピッカー | release.releasedAt |
| マイルストーン選択 | MultiSelect | マイルストーンの複数選択 | release.milestones |
| アセットリンク | FormArray | リンク情報の配列 | release.assets.links |

## イベント仕様

### 1-リリース更新

**トリガー**: 「Save changes」ボタン押下

**処理フロー**:
1. フォームデータを収集
2. GraphQL Mutation `releaseUpdate`を実行
3. 成功時：リリース一覧画面へリダイレクト
4. 失敗時：エラーメッセージを表示

**送信データ形式（GraphQL）**:
```graphql
mutation {
  releaseUpdate(input: {
    projectPath: "namespace/project",
    tagName: "v1.0.0",
    name: "Release Title",
    description: "Release notes...",
    releasedAt: "2025-01-22T00:00:00Z",
    milestones: ["v1.0"],
    assets: {
      links: [
        { name: "Binary", url: "https://..." }
      ]
    }
  }) {
    release { ... }
    errors
  }
}
```

**遷移先**: リリース一覧画面 または 同一画面（エラー時）

### 2-リリース削除

**トリガー**: 「Delete」ボタン押下

**前提条件**:
- `destroy_release`権限を持つ

**処理フロー**:
1. 確認ダイアログを表示
2. 確認後、GraphQL Mutation `releaseDelete`を実行
3. 成功時：リリース一覧画面へリダイレクト
4. 失敗時：エラーメッセージを表示

**遷移先**: リリース一覧画面 または 同一画面（エラー時）

### 3-アセットリンク追加

**トリガー**: 「Add another link」ボタン押下

**処理フロー**: フォームにリンク入力行を追加

**遷移先**: 画面内更新

### 4-アセットリンク削除

**トリガー**: リンク行の削除ボタン押下

**処理フロー**:
1. 既存リンクの場合：削除フラグをセット
2. 新規リンクの場合：配列から削除

**遷移先**: 画面内更新

### 5-キャンセル

**トリガー**: 「Cancel」ボタン押下

**処理フロー**: リリース一覧画面へ遷移

**遷移先**: `/:namespace/:project/-/releases#:tag`

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

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | releases | SELECT | リリース情報の取得 |
| 画面表示 | releases_links | SELECT | アセットリンクの取得 |
| 更新 | releases | UPDATE | リリース情報の更新 |
| 更新 | releases_links | INSERT/UPDATE/DELETE | アセットリンクの更新 |
| 更新 | milestone_releases | INSERT/DELETE | マイルストーン関連の更新 |
| 削除 | releases | DELETE | リリースの削除 |
| 削除 | releases_links | DELETE | 関連アセットの削除 |

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

#### releases

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, project_id, tag, name, description, released_at | project_id = プロジェクトID, tag = パスパラメータ | |
| UPDATE | name, description, released_at | フォーム入力値 | |
| DELETE | - | id = リリースID | カスケード削除 |

#### releases_links

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | release_id, name, url, link_type, filepath | 新規リンク | |
| UPDATE | name, url, link_type, filepath | 既存リンク | |
| DELETE | - | id = リンクID | |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| MSG-001 | エラー | バリデーションエラーメッセージ（配列） | 保存失敗時 |
| MSG-002 | 確認 | Are you sure you want to delete this release? | 削除ボタン押下時 |
| MSG-003 | ラベル | Tag name cannot be changed | タグ名フィールドの説明 |
| MSG-004 | ボタン | Save changes | 保存ボタン |
| MSG-005 | ボタン | Delete | 削除ボタン |

## 例外処理

| 例外 | 原因 | 対処 |
|------|------|------|
| 404 Not Found | 指定されたタグのリリースが存在しない | エラーページ表示 |
| 403 Forbidden | update_release権限がない | アクセス拒否画面へリダイレクト |
| 422 Unprocessable Entity | バリデーションエラー | エラーメッセージを表示 |

## 備考

- タグ名は作成後に変更できない（読み取り専用表示）
- 将来の日時から過去の日時に変更すると「Upcoming Release」から通常リリースに移行
- Markdownプレビュー機能がある
- 削除操作は関連するリンク、マイルストーン関連も削除される
- 削除ドキュメントへのリンクが提供される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | release.rb | `app/models/release.rb` | Releaseモデルの属性、アソシエーションを確認 |

**読解のコツ**: Releaseは`tag`をプライマリ識別子として使用し、変更不可。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | releases_controller.rb | `app/controllers/projects/releases_controller.rb` | editアクション、認可処理 |
| 2-2 | edit.html.haml | `app/views/projects/releases/edit.html.haml` | Vueマウントポイント |
| 2-3 | releases_helper.rb | `app/helpers/releases_helper.rb` | data_for_edit_release_page |

**主要処理フロー**:
1. **8行目**: `authorize_update_release!`で更新権限チェック
2. **14行目**: `before_action :release`でリリース取得
3. **50-56行目** (helper): `data_for_edit_release_page`でVueにデータ渡し

#### Step 3: フロントエンド（Vue.js + GraphQL）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | edit_release_page.vue | `app/assets/javascripts/releases/components/edit_release_page.vue` | 編集画面メインコンポーネント |

**主要処理フロー**:
- GraphQLクエリ`oneRelease`でリリース情報を取得
- GraphQL Mutation `releaseUpdate`で更新実行
- GraphQL Mutation `releaseDelete`で削除実行

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

```
[ブラウザ]
    │
    ├─ GET /releases/:tag/edit (HTML)
    │      └─ ReleasesController#edit
    │             ├─ authorize_update_release!
    │             ├─ @release = Release.find_by_tag
    │             └─ HTML response (Vue mount point)
    │                    └─ data_for_edit_release_page
    │
    ├─ POST /api/graphql (oneRelease Query)
    │      └─ ReleasesFinder.find_by_tag
    │             └─ Release + associations
    │
    ├─ POST /api/graphql (releaseUpdate Mutation)
    │      └─ Mutations::Releases::Update
    │             └─ Releases::UpdateService
    │
    └─ POST /api/graphql (releaseDelete Mutation)
           └─ Mutations::Releases::Delete
                  └─ Releases::DestroyService
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| release.rb | `app/models/release.rb` | モデル | Releaseモデル定義 |
| releases_controller.rb | `app/controllers/projects/releases_controller.rb` | コントローラー | HTTPリクエスト処理 |
| edit.html.haml | `app/views/projects/releases/edit.html.haml` | ビュー | HTMLテンプレート |
| releases_helper.rb | `app/helpers/releases_helper.rb` | ヘルパー | ビューデータ生成 |
| edit_release_page.vue | `app/assets/javascripts/releases/components/edit_release_page.vue` | Vueコンポーネント | 編集画面 |
| update_service.rb | `app/services/releases/update_service.rb` | サービス | 更新ビジネスロジック |
| destroy_service.rb | `app/services/releases/destroy_service.rb` | サービス | 削除ビジネスロジック |
