# 画面設計書 2-ジョブサブミット

## 概要

本ドキュメントは、Apache Flink Web DashboardのジョブサブミットJARファイルのアップロード、ジョブの設定・実行を行う画面の設計仕様を記述したものである。

### 本画面の処理概要

ジョブサブミット画面は、FlinkジョブをクラスターにデプロイするためのJARファイル管理および実行機能を提供する。ユーザーはJARファイルのアップロード、削除、実行パラメータの設定、実行計画のプレビュー、およびジョブの実行を行うことができる。

**業務上の目的・背景**：データパイプラインやストリーム処理ジョブをFlinkクラスターにデプロイする際、開発者や運用者がWebインターフェースを通じて簡便にジョブを投入できる環境を提供する。CLIを使用せずにブラウザからジョブを管理できるため、運用効率が向上し、ジョブ投入のハードルが下がる。

**画面へのアクセス方法**：ナビゲーションメニューの「Submit New Job」をクリックするか、`/submit`パスに直接アクセスする。

**主要な操作・処理内容**：
1. JARファイルのアップロード（プログレス表示付き）
2. アップロード済みJARファイル一覧の表示
3. JARファイルの削除（確認ダイアログ付き）
4. 実行パラメータの設定（Entry Class、Parallelism、Program Arguments、Savepoint Path、Allow Non Restored State）
5. 実行計画（Plan）のプレビュー表示（DAGグラフ形式）
6. ジョブの実行と実行中ジョブ画面への自動遷移

**画面遷移**：
- 遷移元：ナビゲーションメニュー、オーバービュー画面
- 遷移先：実行中ジョブ詳細画面（`/job/running/:jobId`）

**権限による表示制御**：Yarn環境（URLに`/proxy/application_`を含む場合）では、JARアップロード機能が制限され、代替URLへの誘導メッセージが表示される。また、REST APIへのアクセスエラー時は`noAccess`フラグが立ち、機能が制限される。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 32 | REST API | 主機能 | JARファイル一覧取得、アップロード、削除、ジョブ実行のREST API呼び出し |
| 23 | ジョブ管理 | 遷移先機能 | ジョブ実行後の実行中ジョブ画面への遷移 |

## 画面種別

登録 / 実行

## URL/ルーティング

- パス: `/submit`

## 入出力項目

### 入力項目

| 項目名 | 種別 | データ型 | 必須 | 説明 |
|--------|------|----------|------|------|
| JARファイル | ファイル | File | ○（アップロード時） | アップロードするJARファイル |
| Entry Class | テキスト | string | - | 実行するメインクラスの完全修飾名 |
| Parallelism | テキスト | string | - | ジョブの並列度 |
| Program Arguments | テキスト | string | - | プログラム引数 |
| Savepoint Path | テキスト | string | - | セーブポイントのパス（復元時） |
| Allow Non Restored State | チェックボックス | boolean | - | 復元されない状態を許可するか |

## 表示項目

### JARファイル一覧

| 項目名 | データ型 | 説明 | 取得元 |
|--------|----------|------|--------|
| Name | string | JARファイル名 | `/jars` API |
| Upload Time | timestamp | アップロード日時 | `/jars` API |
| Entry Class | string[] | 含まれるエントリークラス一覧 | `/jars` API |
| 削除リンク | action | JARファイル削除アクション | - |

### アップロードプログレス

| 項目名 | データ型 | 説明 |
|--------|----------|------|
| プログレスバー | number | アップロード進捗率（0-100%） |

## イベント仕様

### 1-画面初期化

**トリガー**: コンポーネント初期化時（ngOnInit）

**処理フロー**:
1. Yarn環境かどうかをURLから判定（`/proxy/application_`の存在チェック）
2. フォームの初期化（entryClass, parallelism, programArgs, savepointPath, allowNonRestoredState）
3. StatusService.refresh$をサブスクライブ
4. JarService.loadJarList()でJAR一覧を取得
5. エラー時はnoAccessフラグを設定

**API呼び出し**:
- GET `/jars`

### 2-JARファイルアップロード

**トリガー**: ファイル選択後のアップロードイベント

**処理フロー**:
1. FileReadDirectiveがファイル読み込みを検知
2. JarService.uploadJar()を呼び出し
3. HttpEventType.UploadProgressイベントで進捗率を計算・表示
4. HttpEventType.Responseイベントでアップロード完了を検知
5. StatusService.forceRefresh()で一覧を更新

**API呼び出し**:
- POST `/jars/upload`（multipart/form-data）

### 3-JARファイル削除

**トリガー**: 削除リンククリック後、確認ダイアログで「確認」

**処理フロー**:
1. NzPopconfirmで確認ダイアログ表示
2. 確認後、JarService.deleteJar()を呼び出し
3. StatusService.forceRefresh()で一覧を更新
4. expandedMapから該当JARを削除

**API呼び出し**:
- DELETE `/jars/{jarId}`

### 4-JARファイル展開（パラメータ入力）

**トリガー**: JAR行クリック

**処理フロー**:
1. 他のJARの展開状態をリセット
2. クリックしたJARの展開状態をトグル
3. JARにエントリークラスがあれば、最初のエントリークラスをフォームにセット
4. フォームをリセット

### 5-実行計画プレビュー

**トリガー**: 「Show Plan」ボタンクリック

**処理フロー**:
1. JarService.getPlan()を呼び出し
2. planVisibleをtrueに設定
3. DagreComponentでDAGグラフを描画

**API呼び出し**:
- GET `/jars/{jarId}/plan?entry-class={}&parallelism={}&program-args={}`

### 6-ジョブ実行

**トリガー**: 「Submit」ボタンクリック

**処理フロー**:
1. JarService.runJob()を呼び出し
2. レスポンスからjobidを取得
3. Router.navigate()で実行中ジョブ詳細画面に遷移

**API呼び出し**:
- POST `/jars/{jarId}/run?entry-class={}&parallelism={}&program-args={}&savepointPath={}&allowNonRestoredState={}`

**遷移先**: `/job/running/{jobId}`

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

本画面はREST APIを通じてFlinkクラスターと通信し、直接的なデータベース更新は行わない。

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

| 操作（イベント） | 対象 | 操作種別 | 概要 |
|----------------|------|---------|------|
| JARアップロード | Flink BlobStore | CREATE | JARファイルをBlobStoreに保存 |
| JAR削除 | Flink BlobStore | DELETE | JARファイルをBlobStoreから削除 |
| ジョブ実行 | Flink JobManager | CREATE | ジョブをサブミット |

## メッセージ仕様

| メッセージID | 種別 | メッセージ内容 | 表示条件 |
|-------------|------|---------------|----------|
| yarn-proxy-wait | 情報 | "Yarn's AM proxy doesn't allow file uploads. Please wait while we fetch an alternate url for you to use" | Yarn環境でaddressが未取得の場合 |
| yarn-proxy-redirect | 情報 | "Yarn's AM proxy doesn't allow file uploads. You can visit here to access this functionality." | Yarn環境でaddressが取得済みの場合 |
| delete-confirm | 確認 | "Are you sure delete this jar?" | JAR削除リンククリック時 |

## 例外処理

| 例外種別 | 発生条件 | 処理内容 |
|----------|----------|----------|
| JAR一覧取得エラー | `/jars` APIエラー | noAccessをtrueに設定、isLoadingをfalseに設定 |
| アップロードエラー | アップロード中のエラー | isUploadingをfalse、progressを0にリセット |
| API通信エラー | REST APIへの接続失敗 | catchErrorでエラーオブジェクトを返却（error: true） |

## 備考

- Yarn環境では、AMプロキシの制限によりファイルアップロードが制限される
- アップロード進捗はHttpEventType.UploadProgressを使用してリアルタイム表示
- フォームはReactiveFormsModuleを使用（UntypedFormGroup）
- DAGグラフ表示にはDagreComponentを使用

---

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

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

### 推奨読解順序

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

まず、プログラム間で受け渡されるデータ構造を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | jar.ts | `src/app/interfaces/jar.ts` | JarList型、JarFilesItem型の定義。JAR情報の構造を把握する |
| 1-2 | plan.ts | `src/app/interfaces/plan.ts` | Plan型、PlanDetail型の定義。実行計画のデータ構造を理解する |

**読解のコツ**: JarFilesItemのentryプロパティが配列であり、複数のエントリークラスを持てることに注意。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | submit.component.ts | `src/app/pages/submit/submit.component.ts` | メインコンポーネント。ngOnInit()が処理の起点 |

**主要処理フロー**:
1. **92-93行目**: Yarn環境判定（URLチェック）
2. **94-100行目**: フォーム初期化
3. **101-119行目**: JAR一覧取得のストリーム構築
4. **127-143行目**: JARアップロード処理
5. **188-201行目**: ジョブ実行処理

#### Step 3: サービス層を理解する

データ取得・操作を担当するサービスクラスを解析する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | jar.service.ts | `src/app/services/jar.service.ts` | 全てのJAR関連API呼び出しを担当 |

**主要処理フロー**:
- **34-44行目**: loadJarList() - JAR一覧取得
- **46-53行目**: uploadJar() - JARアップロード（HttpRequestを使用）
- **55-57行目**: deleteJar() - JAR削除
- **59-87行目**: runJob() - ジョブ実行
- **89-122行目**: getPlan() - 実行計画取得

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

HTMLテンプレートでUI構造を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | submit.component.html | `src/app/pages/submit/submit.component.html` | 画面レイアウト、フォーム構造、条件分岐を理解 |

**主要ポイント**:
- **25行目**: isYarnによる条件分岐
- **42-124行目**: JAR一覧テーブルと展開フォーム
- **165-175行目**: Plan表示ドロワー

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

```
SubmitComponent
    │
    ├─ StatusService.refresh$（イベント監視）
    │
    ├─ JarService
    │      ├─ loadJarList() ──▶ GET /jars
    │      ├─ uploadJar() ──▶ POST /jars/upload
    │      ├─ deleteJar() ──▶ DELETE /jars/{jarId}
    │      ├─ getPlan() ──▶ GET /jars/{jarId}/plan
    │      └─ runJob() ──▶ POST /jars/{jarId}/run
    │
    ├─ DagreComponent（子）
    │      └─ DAGグラフ描画
    │
    └─ Router.navigate()
           └─ /job/running/{jobId}
```

### データフロー図

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

JARファイル選択 ─────────▶ uploadJar() ─────────▶ プログレス表示
                                │
                                ▼
StatusService.refresh$ ───────▶ loadJarList() ────▶ JAR一覧表示
                                │
                                ▼
パラメータ入力 ───────────────▶ validateForm ─────▶ フォーム表示
        │
        ├─▶ getPlan() ────────────────────────────▶ DagreComponent
        │
        └─▶ runJob() ─────────────────────────────▶ 画面遷移
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| submit.component.ts | `src/app/pages/submit/submit.component.ts` | ソース | メインコンポーネント |
| submit.component.html | `src/app/pages/submit/submit.component.html` | テンプレート | 画面レイアウト定義 |
| submit.component.less | `src/app/pages/submit/submit.component.less` | スタイル | 画面スタイル定義 |
| jar.service.ts | `src/app/services/jar.service.ts` | ソース | JAR操作サービス |
| jar.ts | `src/app/interfaces/jar.ts` | 型定義 | JARデータ型定義 |
| plan.ts | `src/app/interfaces/plan.ts` | 型定義 | 実行計画型定義 |
| dagre.component.ts | `src/app/components/dagre/dagre.component.ts` | ソース | DAGグラフコンポーネント |
| file-read.directive.ts | `src/app/components/file-read.directive.ts` | ソース | ファイル読み込みディレクティブ |
| status.service.ts | `src/app/services/status.service.ts` | ソース | リフレッシュ管理サービス |
