ensureFirestoreDatabase method

Future<FirestoreInitResult> ensureFirestoreDatabase({
  1. String region = 'nam5',
})

Ensure the default Firestore database exists for projectId.

Runs gcloud firestore databases describe --database=(default) to detect existence; on NOT_FOUND runs gcloud firestore databases create --location=REGION --type=firestore-native.

Defaults to multi-region region nam5 (US). Other common values: • eur3 — multi-region EU • us-central1, europe-west1, asia-northeast1 — regional

Any failure other than NOT_FOUND is surfaced via FirestoreInitResult.message without throwing, so callers can downgrade to a console hand-off.

Implementation

Future<FirestoreInitResult> ensureFirestoreDatabase({
  String region = 'nam5',
}) async {
  if (projectId.isEmpty) {
    return FirestoreInitResult(
      existed: false,
      created: false,
      region: region,
      message: 'No Firebase project ID configured',
    );
  }

  info('Checking Firestore default database for $projectId...');
  final ProcessResult describe = await _runner.run('gcloud', <String>[
    'firestore',
    'databases',
    'describe',
    '--database=(default)',
    '--project=$projectId',
    '--format=json',
  ]);

  if (describe.success) {
    final String? existingRegion = _extractFirestoreRegion(describe.stdout);
    info('Firestore database already exists '
        '${existingRegion != null ? '(region: $existingRegion)' : ''}');
    return FirestoreInitResult(
      existed: true,
      created: false,
      region: existingRegion ?? region,
    );
  }

  final String stderr = describe.stderr;

  // Special-case: API not enabled. We surface a clear hand-off so the
  // wizard can offer "auto-enable + retry".
  if (_isApiNotEnabledError(stderr, 'firestore.googleapis.com')) {
    return FirestoreInitResult(
      existed: false,
      created: false,
      region: region,
      message:
          'Cloud Firestore API is not enabled for $projectId. Enable it via:\n'
          '  • gcloud services enable firestore.googleapis.com --project=$projectId\n'
          '  • or open: ${apiEnableUrl(projectId, 'firestore.googleapis.com')}',
    );
  }

  if (FirebaseBillingService.isBillingAbsentError(stderr)) {
    return FirestoreInitResult(
      existed: false,
      created: false,
      region: region,
      message:
          'Billing is not enabled on $projectId. Upgrade to Blaze first:\n'
          '  ${FirebaseBillingService.upgradeUrl(projectId)}',
    );
  }

  if (!_isNotFoundError(stderr)) {
    // Don't block on permission / auth issues — caller can hand off.
    return FirestoreInitResult(
      existed: false,
      created: false,
      region: region,
      message: stderr.trim().isEmpty
          ? 'gcloud firestore describe exited ${describe.exitCode}'
          : stderr.trim(),
    );
  }

  info('Creating Firestore default database in $region...');
  final ProcessResult create = await _runner.run('gcloud', <String>[
    'firestore',
    'databases',
    'create',
    '--database=(default)',
    '--location=$region',
    '--project=$projectId',
    '--type=firestore-native',
  ]);

  if (create.success) {
    success('Firestore database created (region: $region).');
    return FirestoreInitResult(
      existed: false,
      created: true,
      region: region,
      message: 'Created in $region',
    );
  }

  final String createErr = create.stderr;
  if (FirebaseBillingService.isBillingAbsentError(createErr)) {
    return FirestoreInitResult(
      existed: false,
      created: false,
      region: region,
      message:
          'Billing is not enabled on $projectId. Upgrade to Blaze first:\n'
          '  ${FirebaseBillingService.upgradeUrl(projectId)}',
    );
  }

  return FirestoreInitResult(
    existed: false,
    created: false,
    region: region,
    message: createErr.trim().isEmpty
        ? 'gcloud firestore create exited ${create.exitCode}'
        : createErr.trim(),
  );
}