Skip to content

refactor(cmd): migrate CLI commands from gRPC to ConnectRPC clients#1388

Merged
whoAbhishekSah merged 5 commits intomainfrom
refactor/migrate-cli-to-connectrpc
Feb 19, 2026
Merged

refactor(cmd): migrate CLI commands from gRPC to ConnectRPC clients#1388
whoAbhishekSah merged 5 commits intomainfrom
refactor/migrate-cli-to-connectrpc

Conversation

@whoAbhishekSah
Copy link
Member

@whoAbhishekSah whoAbhishekSah commented Feb 17, 2026

Summary

Migrate all CLI commands from gRPC clients to ConnectRPC clients, decoupling the CLI from the legacy gRPC server.

Changes

  • Replace gRPC client dial with ConnectRPC HTTP client factory
  • Add generic newRequest[T] helper for header propagation
  • Update all response access from res.GetField() to res.Msg.GetField()
  • Remove gRPC connection lifecycle management
  • Default to HTTPS when no scheme is provided
  • Set 30s HTTP timeout on CLI client to prevent indefinite hangs
  • Return error on malformed --header values instead of silently ignoring
  • Update unit tests for ConnectRPC error handling (connection errors are now *connect.Error instead of context.DeadlineExceeded)

Notes

Test plan

  • go build ./... passes
  • go test ./cmd/... passes
  • Manual test: frontier preferences list / frontier preferences get with session cookie

🤖 Generated with Claude Code

Switch all CLI commands to use ConnectRPC clients instead of gRPC clients.
This decouples the CLI from the legacy gRPC server, preparing for its removal.

Key changes:
- cmd/client.go: Replace gRPC dial with ConnectRPC HTTP client factory
- cmd/context.go: Replace gRPC metadata with generic newRequest[T] helper
- All command files: Use connect.NewRequest() / newRequest() patterns
- Response access: res.GetField() -> res.Msg.GetField()
- Remove gRPC connection lifecycle (cancel, defer cancel)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Feb 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview, Comment Feb 18, 2026 9:12am

@coderabbitai
Copy link

coderabbitai bot commented Feb 17, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Infrastructure

    • CLI communication switched from gRPC dialing to HTTP-based connect clients; host inputs are normalized to include an HTTPS scheme when missing.
  • Behavioral

    • Requests now use a unified wrapper and consistently carry optional headers; responses use the wrapper-style payload access, preserving command outputs.
  • Tests

    • CLI tests generalized to expect error presence for scenarios that now surface connection-related errors instead of a specific error value.

Walkthrough

Replaces gRPC dialing with HTTP Connect clients, adds host normalization, simplifies client constructors (removes context/cancel returns), moves header handling into request wrappers, updates all RPC calls to use connect request wrappers and accesses responses via res.Msg, and updates tests to expect error presence where applicable.

Changes

Cohort / File(s) Summary
Core client & request helpers
cmd/client.go, cmd/context.go
Replaced gRPC dial/TLS and context-based cancellation with HTTP connect clients; added ensureHTTPScheme(host); new createClient/createAdminClient return connect client types (no cancel funcs); introduced newRequest/parseHeader moving header logic to request level.
Command handlers (RPC wrapper migration)
cmd/group.go, cmd/namespace.go, cmd/organization.go, cmd/permission.go, cmd/policy.go, cmd/project.go, cmd/role.go, cmd/user.go, cmd/preferences.go
Removed per-call cancel/context wiring; switched RPC invocations to connect.NewRequest or newRequest(...); updated response access from res.GetXxx()res.Msg.GetXxx(); added connectrpc.com/connect imports and adjusted client creation calls to accept host-only.
Seeding & bootstrap
cmd/seed.go
Refactored to use frontierv1beta1connect client types; threads a header string through seeding helpers; updated helper signatures and replaced all RPC calls with newRequest(..., header) and .Msg response accessors.
Tests — error expectation changes
cmd/group_test.go, cmd/namespace_test.go, cmd/organization_test.go, cmd/permission_test.go, cmd/policy_test.go, cmd/project_test.go, cmd/role_test.go
Added wantErr boolean to test tables and adjusted assertions to check for any error when expected; removed unused context imports and adapted test messages.
Build / imports
go.mod, various cmd/*.go
Updated imports to include connectrpc.com/connect and ...connect client packages; removed gRPC/TLS-related imports and context metadata usage across files.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
cmd/user.go (1)

194-201: ⚠️ Potential issue | 🟡 Minor

Column header mismatch: "SLUG" vs GetTitle().

The table header at line 194 says "SLUG" but the value displayed at line 199 is user.GetTitle(). This appears to be a pre-existing issue, but worth noting for consistency.

Proposed fix
-			report = append(report, []string{"ID", "NAME", "EMAIL", "SLUG"})
+			report = append(report, []string{"ID", "NAME", "EMAIL", "TITLE"})
cmd/seed.go (1)

35-37: ⚠️ Potential issue | 🟡 Minor

Global mutable state may cause issues if seed is called multiple times.

The package-level variables resourceNamespaces and roleIDs are appended to during execution without being reset. If SeedCommand is invoked multiple times in the same process (e.g., tests or interactive CLI sessions), stale data from previous runs could accumulate.

Proposed fix: Reset or scope variables locally
-	resourceNamespaces []string
-	roleIDs            []string
 )

 func SeedCommand(cliConfig *Config) *cli.Command {
 	var header, configFile string
+	var resourceNamespaces []string
+	var roleIDs []string

Then pass these as parameters or return values from createCustomRolesAndPermissions.

🧹 Nitpick comments (2)
cmd/context.go (1)

20-27: Consider trimming whitespace from parsed header key and value.

Headers like "Authorization: Bearer token" will result in a value with a leading space (" Bearer token"). Most HTTP clients trim optional whitespace after the colon. Consider using strings.TrimSpace for robustness.

Proposed fix
 func parseHeader(header string) (string, string, bool) {
 	parts := strings.SplitN(header, ":", 2)
 	if len(parts) != 2 {
 		return "", "", false
 	}
-	return parts[0], parts[1], true
+	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), true
 }
cmd/client.go (1)

12-24: Consider configuring HTTP client timeouts.

Using http.DefaultClient means no request timeouts are enforced. If the server is unresponsive, CLI commands could hang indefinitely. Consider creating a client with reasonable timeouts.

Optional: Configure HTTP client with timeouts
+import "time"
+
+var httpClient = &http.Client{
+	Timeout: 30 * time.Second,
+}
+
 func createClient(host string) (frontierv1beta1connect.FrontierServiceClient, error) {
 	if host == "" {
 		return nil, ErrClientConfigHostNotFound
 	}
-	return frontierv1beta1connect.NewFrontierServiceClient(http.DefaultClient, ensureHTTPScheme(host)), nil
+	return frontierv1beta1connect.NewFrontierServiceClient(httpClient, ensureHTTPScheme(host)), nil
 }

 func createAdminClient(host string) (frontierv1beta1connect.AdminServiceClient, error) {
 	if host == "" {
 		return nil, ErrClientConfigHostNotFound
 	}
-	return frontierv1beta1connect.NewAdminServiceClient(http.DefaultClient, ensureHTTPScheme(host)), nil
+	return frontierv1beta1connect.NewAdminServiceClient(httpClient, ensureHTTPScheme(host)), nil
 }

ConnectRPC wraps connection errors in *connect.Error instead of
returning context.DeadlineExceeded directly. Update test assertions
to use wantErr flag for connection-failure cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coveralls
Copy link

coveralls commented Feb 18, 2026

Pull Request Test Coverage Report for Build 22133478615

Details

  • 41 of 279 (14.7%) changed or added relevant lines in 12 files are covered.
  • 29 unchanged lines in 9 files lost coverage.
  • Overall coverage increased (+0.1%) to 38.457%

Changes Missing Coverage Covered Lines Changed/Added Lines %
cmd/namespace.go 5 7 71.43%
cmd/client.go 6 13 46.15%
cmd/policy.go 3 14 21.43%
cmd/group.go 6 19 31.58%
cmd/permission.go 5 18 27.78%
cmd/project.go 6 19 31.58%
cmd/role.go 5 18 27.78%
cmd/context.go 0 15 0.0%
cmd/organization.go 5 22 22.73%
cmd/user.go 0 18 0.0%
Files with Coverage Reduction New Missed Lines %
cmd/seed.go 1 7.45%
cmd/client.go 2 64.86%
cmd/policy.go 2 55.56%
cmd/group.go 4 52.81%
cmd/namespace.go 4 64.86%
cmd/organization.go 4 48.71%
cmd/permission.go 4 54.55%
cmd/project.go 4 52.4%
cmd/role.go 4 51.53%
Totals Coverage Status
Change from base Build 22087710772: 0.1%
Covered Lines: 16200
Relevant Lines: 42125

💛 - Coveralls

@whoAbhishekSah
Copy link
Member Author

whoAbhishekSah commented Feb 18, 2026

Issues found during testing

While manually testing the CLI after the ConnectRPC migration, the following pre-existing issues were discovered:

These are not regressions from this PR — they existed before the migration.

Avoids sending auth headers over unencrypted HTTP by default.
Users can still explicitly use http:// for local development.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
parseHeader and newRequest now return errors when the header string
is invalid (missing colon separator), preventing silent failures
when users pass malformed --header values.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
cmd/client.go (1)

12-23: Set a bounded HTTP timeout instead of http.DefaultClient.

http.DefaultClient has no request timeout, so CLI calls can hang indefinitely if the server stalls. The codebase already uses this pattern elsewhere (e.g., cmd/serve.go creates a client with an 80-second timeout). Apply the same approach here with a reasonable timeout value.

♻️ Suggested update with a bounded timeout
 import (
 	"fmt"
 	"net/http"
 	"strings"
+	"time"
 
 	frontierv1beta1connect "github.com/raystack/frontier/proto/v1beta1/frontierv1beta1connect"
 	"github.com/spf13/cobra"
 )
 
+var defaultHTTPClient = &http.Client{
+	Timeout: 30 * time.Second,
+}
+
 func createClient(host string) (frontierv1beta1connect.FrontierServiceClient, error) {
 	if host == "" {
 		return nil, ErrClientConfigHostNotFound
 	}
-	return frontierv1beta1connect.NewFrontierServiceClient(http.DefaultClient, ensureHTTPScheme(host)), nil
+	return frontierv1beta1connect.NewFrontierServiceClient(defaultHTTPClient, ensureHTTPScheme(host)), nil
 }
 
 func createAdminClient(host string) (frontierv1beta1connect.AdminServiceClient, error) {
 	if host == "" {
 		return nil, ErrClientConfigHostNotFound
 	}
-	return frontierv1beta1connect.NewAdminServiceClient(http.DefaultClient, ensureHTTPScheme(host)), nil
+	return frontierv1beta1connect.NewAdminServiceClient(defaultHTTPClient, ensureHTTPScheme(host)), nil
 }

Replace http.DefaultClient (no timeout) with a client configured
with a 30-second timeout to prevent CLI commands from hanging
indefinitely if the server stalls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@whoAbhishekSah
Copy link
Member Author

@CodeRabbit review

@coderabbitai
Copy link

coderabbitai bot commented Feb 18, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
cmd/client.go (1)

31-36: Trim whitespace before scheme normalization.

If host comes from config/flags with leading/trailing spaces, it will bypass the scheme check and produce malformed URLs. A quick trim makes the helper more robust.

🔧 Suggested tweak
 func ensureHTTPScheme(host string) string {
+	host = strings.TrimSpace(host)
 	if strings.HasPrefix(host, "http://") || strings.HasPrefix(host, "https://") {
 		return host
 	}
 	return fmt.Sprintf("https://%s", host)
 }

@whoAbhishekSah
Copy link
Member Author

Issues discovered during testing and review:

@whoAbhishekSah whoAbhishekSah merged commit 2fd15c2 into main Feb 19, 2026
7 checks passed
@whoAbhishekSah whoAbhishekSah deleted the refactor/migrate-cli-to-connectrpc branch February 19, 2026 05:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments