body method

  1. @override
String body(
  1. String baseName,
  2. String className
)
override

Defines the actual body code. path is passed relative to lib, baseName is the filename, and className is the filename converted to Pascal case.

実際の本体コードを定義します。pathlibからの相対パス、baseNameにファイル名が渡され、classNameにファイル名をパスカルケースに変換した値が渡されます。

Implementation

@override
String body(String baseName, String className) {
  return """
`Firebase Data Connect`は下記のように利用する。

## 概要

$excerpt

Firebase Data ConnectはPostgreSQLをバックエンドとし、GraphQLでデータにアクセスします。Masamuneモデルから自動的にスキーマ、クエリ、ミューテーションを生成します。

**パッケージ構成**:
- `masamune_model_firebase_data_connect` - ランタイムアダプターとコンバーター
- `masamune_model_firebase_data_connect_annotation` - アノテーション(`@firebaseDataConnect`)
- `masamune_model_firebase_data_connect_builder` - コードジェネレーター

## 設定方法

### katana.yamlを使用する場合(推奨)

1. `katana.yaml`に下記の設定を追加。

  ```yaml
  # katana.yaml

  firebase:
    project_id: your-project-id  # FirebaseプロジェクトID

    # Enable Firebase Data Connect.
    # Firebase Data Connectを有効にします。
    dataconnect:
      enable: true # Firebase Data Connectを利用する場合false -> trueに変更
  ```

2. 下記のコマンドを実行して設定を適用。

  ```bash
  katana apply
  ```

3. モデルに`@firebaseDataConnect`アノテーションとパーミッション設定を追加。

  ```dart
  // lib/models/user.dart

  import 'package:freezed_annotation/freezed_annotation.dart';
  import 'package:masamune/masamune.dart';
  import 'package:masamune_model_firebase_data_connect_annotation/masamune_model_firebase_data_connect_annotation.dart';

  part 'user.m.dart';
  part 'user.g.dart';
  part 'user.freezed.dart';
  part 'user.dataconnect.dart';  // 生成されるファイル

  @freezed
  @formValue
  @immutable
  @firebaseDataConnect  // Data Connect対応を有効化
  @CollectionModelPath(
    'user',
    permission: [
      // 読み取り権限: 全ユーザーが読み取り可能
      AllowReadModelPermissionQuery.allUsers(),
      // 書き込み権限: 全ユーザーが書き込み可能
      AllowWriteModelPermissionQuery.allUsers(),
    ],
  )
  class UserModel with _\$UserModel {
    const factory UserModel({
      required String name,
      @Default('') String email,
      @Default(ModelTimestamp.now()) ModelTimestamp createdAt,
    }) = _UserModel;

    const UserModel._();

    factory UserModel.fromJson(Map<String, Object?> json) => _\$UserModelFromJson(json);

    static const document = _\$UserModelDocumentQuery();
    static const collection = _\$UserModelCollectionQuery();
  }
  ```

4. GraphQLスキーマを生成。

  ```bash
  katana code generate
  ```

5. スキーマをFirebaseにデプロイ。

  ```bash
  katana deploy
  ```

6. 必要に応じてマイグレーションを実行。

  ```bash
  firebase dataconnect:sql:migrate
  ```

### 手動でパッケージを追加する場合

1. パッケージをプロジェクトに追加。

  ```bash
  flutter pub add masamune_model_firebase_data_connect
  flutter pub add masamune_model_firebase_data_connect_annotation
  flutter pub add --dev masamune_model_firebase_data_connect_builder
  flutter pub add --dev build_runner
  ```

2. 以降は「katana.yamlを使用する場合」の手順3以降と同じです。

## 生成されるファイル

`katana code generate`を実行すると、`firebase/dataconnect/`に以下が生成されます:

### GraphQLスキーマ (`schema.gql`)

```graphql
type User @table {
id: String!
name: String!
email: String!
createdAt: Timestamp!
}
```

### クエリ (`queries.gql`)

```graphql
query getUserById(\$id: String!) @auth(level: USER) {
user(id: \$id) {
  id
  name
  email
  createdAt
}
}

query listUsers @auth(level: USER) {
users {
  id
  name
  email
}
}
```

### ミューテーション (`mutations.gql`)

```graphql
mutation createUser(\$id: String!, \$name: String!, \$email: String!) @auth(level: USER) {
user_insert(data: {
  id: \$id
  name: \$name
  email: \$email
})
}

mutation updateUser(\$id: String!, \$name: String, \$email: String) @auth(level: USER) {
user_update(id: \$id, data: {
  name: \$name
  email: \$email
})
}

mutation deleteUser(\$id: String!) @auth(level: USER) {
user_delete(id: \$id)
}
```

### コネクター設定 (`connector.yaml`)

```yaml
connectorId: default
generate:
dartSdk:
  outputDir: ../lib/generated
```

### アダプター実装 (`user.dataconnect.dart`)

```dart
part of 'user.dart';

mixin _\$UserModelFirebaseDataConnectAdapter
  on FirebaseDataConnectModelAdapterBase {
// 生成されたアダプター実装
}
```

## 利用方法

### データの読み込み

他のMasamuneモデルと同様に使用:

```dart
class UserListPage extends PageScopedWidget {
@override
Widget build(BuildContext context, PageRef ref) {
  final users = ref.app.model(UserModel.collection())..load();

  return ListView.builder(
    itemCount: users.length,
    itemBuilder: (context, index) {
      final user = users[index].value;
      return ListTile(
        title: Text(user?.name ?? ''),
        subtitle: Text(user?.email ?? ''),
      );
    },
  );
}
}
```

### データの作成・更新

```dart
// 新規作成
final collection = ref.app.model(UserModel.collection());
final newDoc = collection.create();
await newDoc.save(
UserModel(
  name: "John Doe",
  email: "john@example.com",
),
);

// 更新
final doc = ref.app.model(UserModel.document("user_id"));
await doc.load();
await doc.save(
doc.value!.copyWith(name: "Jane Doe"),
);
```

### データの削除

```dart
final doc = ref.app.model(UserModel.document("user_id"));
await doc.delete();
```

## フィールド値コンバーター

パッケージはMasamuneフィールドタイプ用のコンバーターを含みます:

- `ModelTimestamp` / `ModelTimestampRange`
- `ModelDate` / `ModelDateRange` / `ModelTime` / `ModelTimeRange`
- `ModelUri` / `ModelImageUri` / `ModelVideoUri`
- `ModelRef` (参照)
- `ModelGeoValue` (地理座標)
- `ModelVectorValue` (ベクトル)
- `ModelCounter` / `ModelToken`
- `ModelLocale` / `ModelLocalizedValue`
- その他...

これらはスキーマ生成とデータ変換時に自動的に適用されます。

## パーミッション設定

`@CollectionModelPath`でモデルのアクセス権限を定義:

### 全ユーザーにアクセスを許可

```dart
@CollectionModelPath(
'user',
permission: [
  AllowReadModelPermissionQuery.allUsers(),    // 全ユーザーが読み取り可能
  AllowWriteModelPermissionQuery.allUsers(),   // 全ユーザーが書き込み可能
],
)
class UserModel with _\$UserModel { ... }
```

### 認証済みユーザーのみアクセス許可

```dart
@CollectionModelPath(
'post',
permission: [
  AllowReadModelPermissionQuery.authUsers(),   // 認証済みユーザーのみ読み取り可能
  AllowWriteModelPermissionQuery.authUsers(),  // 認証済みユーザーのみ書き込み可能
],
)
class PostModel with _\$PostModel { ... }
```

### ユーザー自身のデータのみアクセス許可

```dart
@CollectionModelPath(
'profile',
permission: [
  AllowReadModelPermissionQuery.user(),   // ユーザー自身のみ読み取り可能
  AllowWriteModelPermissionQuery.user(),  // ユーザー自身のみ書き込み可能
],
)
class ProfileModel with _\$ProfileModel { ... }
```

### 読み取りのみ許可

```dart
@CollectionModelPath(
'news',
permission: [
  AllowReadModelPermissionQuery.allUsers(),  // 全ユーザーが読み取り可能
  // 書き込み権限なし(管理者のみバックエンドから操作)
],
)
class NewsModel with _\$NewsModel { ... }
```

## カスタム設定

`@FirebaseDataConnectAdapter`でカスタム設定を指定:

```dart
@FirebaseDataConnectAdapter(
connector: "my-connector",     // コネクター名
service: "my-service",         // サービス名
location: "us-central1",       // クラウドロケーション
outputDirectory: "firebase/dataconnect",  // 出力パス
)
@firebaseDataConnect
@CollectionModelPath('post')
class PostModel with _\$PostModel {
// モデル定義
}
```

## Firebaseへのデプロイ

生成されたファイルをFirebase Data Connectにデプロイ:

```bash
# katanaコマンドでデプロイ(推奨)
katana deploy

# または直接Firebase CLIを使用
firebase deploy --only dataconnect

# マイグレーションが必要な場合
firebase dataconnect:sql:migrate
```

## build.yamlでの設定

`build.yaml`で生成をカスタマイズ:

```yaml
targets:
\$default:
  builders:
    masamune_model_firebase_data_connect_builder:
      options:
        output_directory: "firebase/dataconnect"
        connector_id: "my-connector"
```

## Firebase Console設定

1. **Firebase Data Connectの有効化**:
 - Firebase Console → Build → Data Connect
 - 「Get started」をクリック

2. **PostgreSQLインスタンスの作成**:
 - リージョンを選択
 - インスタンス名を設定
 - 作成を実行

3. **スキーマのデプロイ**:
 - ローカルで`firebase deploy --only dataconnect`を実行
 - Firebase Consoleでスキーマを確認

## Data Connect Studioでのテスト

1. **Data Connect Studioを開く**:
 ```bash
 firebase dataconnect:studio
 ```

2. **クエリをテスト**:
 - Studioでクエリを記述
 - 実行してレスポンスを確認
 - パラメータを調整してテスト

3. **ミューテーションをテスト**:
 - データの作成・更新・削除をテスト
 - レスポンスを確認

## トラブルシューティング

### スキーマ生成エラー

**原因**: モデルに`@firebaseDataConnect`アノテーションがない

**解決方法**:
1. モデルに`@firebaseDataConnect`を追加
2. `part 'model_name.dataconnect.dart';`を追加
3. `katana code generate`を再実行

### デプロイエラー

**原因**: Firebase CLIがインストールされていない、またはログインしていない

**解決方法**:
```bash
# Firebase CLIをインストール
npm install -g firebase-tools

# ログイン
firebase login

# プロジェクトを初期化
firebase init dataconnect

# katanaコマンドでデプロイ
katana deploy
```

### マイグレーションエラー

**原因**: スキーマ変更がPostgreSQLに反映されていない

**解決方法**:
```bash
# マイグレーションを実行
firebase dataconnect:sql:migrate

# 変更を確認
firebase dataconnect:sql:diff
```

### クエリエラー

**原因**: スキーマとクエリが同期していない

**解決方法**:
1. `katana code generate`でスキーマを再生成
2. `firebase deploy --only dataconnect`で再デプロイ
3. アプリを再起動

## 実装例: リアルタイム同期

```dart
class UserProfilePage extends PageScopedWidget {
const UserProfilePage({required this.userId});

final String userId;

@override
Widget build(BuildContext context, PageRef ref) {
  final user = ref.app.model(UserModel.document(userId))..load();

  // リアルタイム更新を監視
  user.addListener(() {
    if (user.value != null) {
      print("ユーザー情報が更新されました: \${user.value!.name}");
    }
  });

  return Scaffold(
    appBar: AppBar(title: Text(user.value?.name ?? "読み込み中...")),
    body: user.value == null
        ? const Center(child: CircularProgressIndicator())
        : Column(
            children: [
              Text("名前: \${user.value!.name}"),
              Text("メール: \${user.value!.email}"),
              ElevatedButton(
                onPressed: () async {
                  await user.save(
                    user.value!.copyWith(name: "新しい名前"),
                  );
                },
                child: const Text("名前を更新"),
              ),
            ],
          ),
  );
}
}
```

### Tips

- Data Connect対応したいモデルすべてに`@firebaseDataConnect`を付与
- `@CollectionModelPath`でパーミッション設定を必ず指定
- モデル変更後は必ず`katana code generate`を実行
- デプロイには`katana deploy`コマンドを使用(推奨)
- スキーマ変更時は`firebase dataconnect:sql:migrate`でマイグレーション
- デプロイ前に生成されたGraphQLをレビュー
- Firebase Data Connect Studioでクエリをテスト
- Firebase Consoleでクエリパフォーマンスを監視
- スキーマファイルはバージョン管理に含める
- PostgreSQLの制限事項を理解してモデル設計
- 複雑なクエリはカスタムGraphQLで実装可能
- パーミッション設定でセキュリティを適切に管理
""";
}