clientFromDiscovery function

Future<KumihoClient> clientFromDiscovery({
  1. String? token,
  2. String? tenantHint,
  3. String? controlPlaneUrl,
  4. bool forceRefresh = false,
})

Creates a gRPC KumihoClient using Control Plane discovery.

  • Uses discovery to find the correct data-plane endpoint.
  • Sets tenantId so the base client injects x-tenant-id.
  • If token is omitted, the client falls back to the standard token loader.

As a cloud-safe fallback, when no token can be resolved and no explicit controlPlaneUrl is supplied, a loopback self-hosted CE server is probed first. If one is present a tokenless CE client is returned; otherwise the normal cloud discovery path runs (and surfaces its usual "token required" error). The CE probe never runs when a token or explicit endpoint is present, preserving the cloud path unchanged.

Implementation

Future<KumihoClient> clientFromDiscovery({
  String? token,
  String? tenantHint,
  String? controlPlaneUrl,
  bool forceRefresh = false,
}) async {
  final hasExplicitEndpoint =
      controlPlaneUrl != null && controlPlaneUrl.trim().isNotEmpty;
  final resolvedToken =
      (token != null && token.trim().isNotEmpty) ? token : loadBearerToken();
  // A user authenticated only via KUMIHO_FIREBASE_ID_TOKEN is a valid cloud
  // credential that discoverTenant honours, but loadBearerToken() does not read
  // that env var. Treat it (and the cached Firebase id token) as "has a token"
  // too, otherwise such a user would be silently routed to a loopback CE server.
  final firebaseToken = loadFirebaseToken();
  final hasCloudToken =
      (resolvedToken != null && resolvedToken.trim().isNotEmpty) ||
          (firebaseToken != null && firebaseToken.trim().isNotEmpty);

  if (!hasExplicitEndpoint && !hasCloudToken) {
    final ceClient = await clientFromLocalCe();
    if (ceClient != null) {
      return ceClient;
    }
  }

  final record = await discoverTenant(
    controlPlaneUrl: controlPlaneUrl,
    tenantHint: tenantHint,
    forceRefresh: forceRefresh,
  );

  final uri = record.serverUrl;
  final secure = uri.scheme.toLowerCase() == 'https';
  final port = uri.hasPort ? uri.port : (secure ? 443 : 80);

  return KumihoClient(
    host: uri.host,
    port: port,
    secure: secure,
    token: token,
    tenantId: record.tenantId,
  );
}