Skip to content

refactor(gateway-controller): split handlers.go into per-resource files #1657

Merged
renuka-fernando merged 4 commits intowso2:mainfrom
renuka-fernando:fix-1649
Apr 8, 2026
Merged

refactor(gateway-controller): split handlers.go into per-resource files #1657
renuka-fernando merged 4 commits intowso2:mainfrom
renuka-fernando:fix-1649

Conversation

@renuka-fernando
Copy link
Copy Markdown
Contributor

@renuka-fernando renuka-fernando commented Apr 7, 2026

Purpose

Related Issue: #1649

Summary by CodeRabbit

  • New Features

    • API key management for APIs, LLM providers, LLM proxies.
    • LLM provider, LLM provider template, LLM proxy, MCP proxy CRUD management.
    • Secret management (CRUD).
    • Subscription and subscription plan management.
    • WebSub API endpoints added (currently return "not implemented").
  • Chores

    • Updated GitHub Actions Go caching configuration across CI workflows.

Extract handler methods from the monolithic handlers.go into separate
files per resource type, following the existing rest_api_handler.go
pattern:

- api_key_handler.go — REST API key CRUD + resolveAPIIDByHandle
- llm_provider_handler.go — LLM provider CRUD + provider API keys
- llm_provider_template_handler.go — LLM provider template CRUD
- llm_proxy_handler.go — LLM proxy CRUD + proxy API keys
- mcp_proxy_handler.go — MCP proxy CRUD + convertAPIPolicy
- secret_handler.go — secret CRUD
- subscription_handler.go — subscription CRUD + response helpers
- subscription_plan_handler.go — subscription plan CRUD
- websub_api_handler.go — WebSub API CRUD

handlers.go now contains only the APIServer struct, NewAPIServer,
core infrastructure methods, and shared helpers.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 7, 2026

Walkthrough

Adds many new Gin HTTP handlers for API keys, LLM providers/templates/proxies, MCP proxies, secrets, subscriptions and subscription plans; updates Go caching inputs in multiple GitHub Actions workflows; and introduces/standardizes sentinel errors and wrapped error returns in LLM and API-key utilities.

Changes

Cohort / File(s) Summary
GitHub Actions: enable cache-by-go.sum
​.github/workflows/cli-gw-build-periodic.yml, ​.github/workflows/codecov.yml, ​.github/workflows/gateway-integration-test-postgres.yml, ​.github/workflows/gateway-integration-test.yml, ​.github/workflows/operator-integration-test.yml, ​.github/workflows/platform-api-pr-check.yml, ​.github/workflows/sample-service-pr-check.yml, ​.github/workflows/sdk-integration-test.yml
Added cache-dependency-path: '**/go.sum' to actions/setup-go@v5 steps to key dependency caches to repository go.sum files.
GitHub Actions: disable setup-go cache
​.github/workflows/cli-release.yml, ​.github/workflows/gateway-release.yml, ​.github/workflows/platform-api-release.yml, ​.github/workflows/sample-service-release.yml, ​.github/workflows/go-scan.yaml
Set cache: false in actions/setup-go@v5 for release and scan workflows to disable the action’s builtin Go caching.
API Key Handlers
gateway/gateway-controller/pkg/api/handlers/api_key_handler.go
New APIServer handlers for create/revoke/update/regenerate/list API keys with auth extraction, correlation logging, validation, service calls, and HTTP error mapping.
LLM Provider Handlers
gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go
CRUD for LLM providers plus provider-scoped API-key endpoints; raw body forwarding, delegation to llmDeploymentService, error mapping, timestamps, and conditional async deployment pushes.
LLM Provider Template Handlers
gateway/gateway-controller/pkg/api/handlers/llm_provider_template_handler.go
CRUD for LLM provider templates with body parsing, validation error wrapping, conflict handling, list/get/update/delete flows and timestamped responses.
LLM Proxy Handlers
gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go
CRUD for LLM proxies and proxy-scoped API keys; includes raw request handling, validation mapping, optional deployment push, and formatted metadata responses.
MCP Proxy Handlers
gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go
MCP proxy CRUD handlers and convertAPIPolicy helper to map API policies to policy-engine instances; supports conditional search/list and deployment push logic.
Secret Handlers
gateway/gateway-controller/pkg/api/handlers/secret_handler.go
Secret CRUD handlers with raw body processing, correlation ID usage, service guards, storage error mapping, and Kubernetes-like configuration responses.
Subscription Handlers
gateway/gateway-controller/pkg/api/handlers/subscription_handler.go
Subscription CRUD with apiId resolution (handle or deployment id), plan validation for REST APIs, token handling (token returned only on create), and response mapping helpers.
Subscription Plan Handlers
gateway/gateway-controller/pkg/api/handlers/subscription_plan_handler.go
Subscription-plan CRUD with validateThrottleLimits, partial updates, status validation, and DTO mapping.
WebSub API Stubs
gateway/gateway-controller/pkg/api/handlers/websub_api_handler.go
Added WebSub API endpoints that return HTTP 501 Not Implemented with structured error responses.
Error / Utility Updates
gateway/gateway-controller/pkg/utils/api_key.go, gateway/gateway-controller/pkg/utils/llm_deployment.go, gateway/gateway-controller/pkg/utils/llm_transformer.go
API-key utilities now wrap "not found" cases with storage.ErrNotFound; LLM deployment utilities add exported sentinel errors (ErrLLMTemplateNotFound, ErrLLMTemplateValidation, ErrLLMProxyValidation) and wrap validation/conflict/not-found errors; removed an internal transformer helper and inlined provider lookup.
Integration Tests (features)
gateway/it/features/llm-provider-templates.feature, gateway/it/features/llm-proxies.feature
Updated negative test expectations: update non-existent template/proxy scenarios now expect 404 instead of 400.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant APIServer
    participant LLMService as LLM Deployment Service
    participant DB
    participant ControlPlane

    Client->>APIServer: POST /llm-proxies (raw body)
    APIServer->>APIServer: extract correlation, logger
    APIServer->>LLMService: CreateLLMProxy(content, content-type, origin, correlation)
    LLMService->>DB: Save proxy config
    DB-->>LLMService: saved metadata (handle, createdAt)
    LLMService-->>APIServer: success (handle, createdAt, staleFlag)
    alt not stale AND ControlPlane connected AND push enabled
        APIServer->>ControlPlane: waitForDeploymentAndPush(handle)
        ControlPlane-->>APIServer: push result (async)
    end
    APIServer-->>Client: 201 Created (id/createdAt)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

I hopped through handlers, swift and bright,
Keys, proxies, templates — all in sight,
Workflows tuned to cache with care,
Sentinel errors now declare,
A bunny's cheer for code laid right. 🐇✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is severely incomplete. It only provides a Purpose section with an issue reference (#1649), but is missing all other required sections: Goals, Approach, User stories, Documentation, Automation tests, Security checks, Samples, Related PRs, and Test environment. Provide a comprehensive description addressing all template sections: Goals (what problem is solved), Approach (how files were split), test coverage, documentation impact, and test environment details.
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main refactoring objective: splitting a large handlers.go file into multiple per-resource handler files, which aligns with the file additions and code organization shown in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@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: 5

🧹 Nitpick comments (11)
gateway/gateway-controller/pkg/api/handlers/websub_api_handler.go (1)

30-71: Consider consolidating repeated 501 response code into a shared helper.

Lines 30-71 duplicate the same payload in every handler. A small helper + constant will reduce drift risk when message/status changes.

♻️ Suggested cleanup
+const webSubNotImplementedMessage = "WebSub API management is not implemented"
+
+func (s *APIServer) respondWebSubNotImplemented(c *gin.Context) {
+	c.JSON(http.StatusNotImplemented, api.ErrorResponse{
+		Status:  "error",
+		Message: webSubNotImplementedMessage,
+	})
+}
+
 func (s *APIServer) CreateWebSubAPI(c *gin.Context) {
-	c.JSON(http.StatusNotImplemented, api.ErrorResponse{
-		Status:  "error",
-		Message: "WebSub API management is not implemented",
-	})
+	s.respondWebSubNotImplemented(c)
 }
 
 func (s *APIServer) ListWebSubAPIs(c *gin.Context, params api.ListWebSubAPIsParams) {
-	c.JSON(http.StatusNotImplemented, api.ErrorResponse{
-		Status:  "error",
-		Message: "WebSub API management is not implemented",
-	})
+	s.respondWebSubNotImplemented(c)
 }
 
 func (s *APIServer) GetWebSubAPIById(c *gin.Context, id string) {
-	c.JSON(http.StatusNotImplemented, api.ErrorResponse{
-		Status:  "error",
-		Message: "WebSub API management is not implemented",
-	})
+	s.respondWebSubNotImplemented(c)
 }
 
 func (s *APIServer) UpdateWebSubAPI(c *gin.Context, id string) {
-	c.JSON(http.StatusNotImplemented, api.ErrorResponse{
-		Status:  "error",
-		Message: "WebSub API management is not implemented",
-	})
+	s.respondWebSubNotImplemented(c)
 }
 
 func (s *APIServer) DeleteWebSubAPI(c *gin.Context, id string) {
-	c.JSON(http.StatusNotImplemented, api.ErrorResponse{
-		Status:  "error",
-		Message: "WebSub API management is not implemented",
-	})
+	s.respondWebSubNotImplemented(c)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/websub_api_handler.go` around
lines 30 - 71, Multiple handlers (CreateWebSubAPI, ListWebSubAPIs,
GetWebSubAPIById, UpdateWebSubAPI, DeleteWebSubAPI) return the same 501 JSON
payload repeatedly; extract a shared constant for the status/message and a small
helper method (e.g., writeNotImplemented(c *gin.Context) or
s.writeNotImplemented) that sends c.JSON(http.StatusNotImplemented,
api.ErrorResponse{...}) and replace each handler body to call that helper to
avoid duplication and reduce drift risk.
.github/workflows/sample-service-pr-check.yml (1)

23-23: Include workspace files in cache key to avoid stale cache hits.

The cache-dependency-path on line 23 only hashes **/go.sum, but this repo uses a Go workspace with go.work and go.work.sum at the root. Since samples/sample-service (the workspace member) has no local go.sum file, workspace dependency changes tracked in go.work.sum won't invalidate the cache. Add go.work and go.work.sum to the cache key.

💡 Proposed update
-          cache-dependency-path: '**/go.sum'
+          cache-dependency-path: |
+            **/go.sum
+            go.work
+            go.work.sum
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/sample-service-pr-check.yml at line 23, Update the cache
key configuration so workspace files are included: modify the
cache-dependency-path entry (symbol: cache-dependency-path) to include go.work
and go.work.sum in addition to **/go.sum, ensuring changes in the
repository-level workspace files will invalidate the cache and avoid stale cache
hits for workspace members like samples/sample-service.
gateway/gateway-controller/pkg/api/handlers/secret_handler.go (3)

91-93: Inconsistent logger usage: some handlers use s.logger directly.

ListSecrets, GetSecret, and DeleteSecret use s.logger directly (lines 92, 148, 310), while CreateSecret and UpdateSecret use middleware.GetLogger(c, s.logger). The middleware version includes request-scoped context. Consider using middleware.GetLogger consistently.

♻️ Suggested fix for ListSecrets
 func (s *APIServer) ListSecrets(c *gin.Context) {
-	log := s.logger
+	log := middleware.GetLogger(c, s.logger)
 	correlationID := middleware.GetCorrelationID(c)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/secret_handler.go` around lines
91 - 93, ListSecrets, GetSecret, and DeleteSecret use s.logger directly while
CreateSecret and UpdateSecret use middleware.GetLogger(c, s.logger); update the
former three to obtain a request-scoped logger by calling
middleware.GetLogger(c, s.logger) at the start of each handler (e.g., in
ListSecrets, GetSecret, DeleteSecret) and replace uses of s.logger in those
functions with the returned log variable so all handlers consistently include
request context.

368-369: Consider returning a response body for DELETE success.

DeleteSecret returns c.Status(http.StatusOK) with no body (line 369), while other handlers return JSON responses. For API consistency and client debugging, consider returning a JSON body similar to other delete handlers.

🔧 Suggested fix
-	// Return 200 OK on successful deletion
-	c.Status(http.StatusOK)
+	// Return 200 OK on successful deletion
+	c.JSON(http.StatusOK, gin.H{
+		"status":  "success",
+		"message": "Secret deleted successfully",
+		"id":      id,
+	})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/secret_handler.go` around lines
368 - 369, The DELETE handler DeleteSecret currently responds with
c.Status(http.StatusOK) and no body; change it to return a JSON response
consistent with other handlers (use c.JSON(http.StatusOK, ...) ) containing a
clear status and identifying info (e.g., {"status":"ok","message":"secret
deleted","id": <secretID>} or similar), retrieving the secret ID from the
request/context the same way DeleteSecret currently uses; ensure the response
shape matches existing delete handlers for consistency.

67-75: Misleading error log message.

The log message "Failed to encrypt Secret" (line 68) is misleading since CreateSecret can fail for parsing, validation, conflict, or persistence reasons—not just encryption. Consider a more accurate message like "Failed to create secret".

🔧 Suggested fix
 	if err != nil {
-		log.Error("Failed to encrypt Secret", slog.Any("error", err))
+		log.Error("Failed to create secret", slog.Any("error", err))
 		if storage.IsConflictError(err) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/secret_handler.go` around lines
67 - 75, The log message in the CreateSecret handler is misleading—replace the
specific "Failed to encrypt Secret" message with a more accurate, generic
message such as "Failed to create secret" (while still logging the error with
slog.Any) in the error handling block that uses storage.IsConflictError and
c.JSON to return the response; update the slog.Error call so it reads a generic
"Failed to create secret" and retains slog.Any("error", err) for context.
gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go (3)

238-254: Log level mismatch: Warn used before error classification.

Line 240 logs at Warn level regardless of error type, but line 249 returns HTTP 500 for non-"not found" errors. Consider logging at appropriate level after classifying the error.

🔧 Suggested fix
 	cfg, err := s.llmDeploymentService.DeleteLLMProvider(id, correlationID, log)
 	if err != nil {
-		log.Warn("Failed to delete LLM provider configuration", slog.String("handle", id))
 		// Check if it's a not found error
 		if strings.Contains(err.Error(), "not found") {
+			log.Warn("LLM provider configuration not found for deletion", slog.String("handle", id))
 			c.JSON(http.StatusNotFound, api.ErrorResponse{
 				Status:  "error",
 				Message: err.Error(),
 			})
 			return
 		}
+		log.Error("Failed to delete LLM provider configuration", slog.String("handle", id), slog.Any("error", err))
 		c.JSON(http.StatusInternalServerError, api.ErrorResponse{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go` around
lines 238 - 254, The handler currently logs a warning before classifying the
error from llmDeploymentService.DeleteLLMProvider; update the flow to classify
the error first (check strings.Contains(err.Error(), "not found")) and then log
at the appropriate level: use log.Warn (or Info) including context for "not
found" cases and log.Error (including the error details and
correlationID/handle) for other errors before returning the corresponding HTTP
response in the DeleteLLMProvider handler.

50-57: Silently ignored json.Marshal error could mask data corruption.

The error from json.Marshal on line 50 is discarded. While unlikely to fail for in-memory structs, silently ignoring it could mask issues with unexpected types in cfg.SourceConfiguration.

🔧 Suggested fix
-		j, _ := json.Marshal(cfg.SourceConfiguration)
-		if err := json.Unmarshal(j, &prov); err != nil {
+		j, marshalErr := json.Marshal(cfg.SourceConfiguration)
+		if marshalErr != nil {
+			log.Error("Failed to marshal stored LLM provider configuration",
+				slog.String("uuid", cfg.UUID), slog.Any("error", marshalErr))
+			c.JSON(http.StatusInternalServerError, api.ErrorResponse{Status: "error",
+				Message: "Failed to get stored LLM provider configuration"})
+			return
+		}
+		if err := json.Unmarshal(j, &prov); err != nil {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go` around
lines 50 - 57, The json.Marshal error is being ignored for
cfg.SourceConfiguration; change the code to capture and handle that error before
proceeding to json.Unmarshal: replace the ignored marshal with j, err :=
json.Marshal(cfg.SourceConfiguration) and if err != nil log the failure (use the
same logger/slog context as the existing unmarshalling error, include cfg.UUID
and the err), return an HTTP 500 via c.JSON with an appropriate ErrorResponse,
and only call json.Unmarshal(&prov) when the marshal succeeds.

141-158: Inconsistent "not found" error detection across handlers.

GetLLMProviderById uses both storage.IsNotFoundError(err) and strings.Contains (line 142), while DeleteLLMProvider (line 242) only uses strings.Contains. Consider standardizing on storage.IsNotFoundError for type-safe error checking.

♻️ Suggested consistency fix
 	if err != nil {
-		if !storage.IsNotFoundError(err) && !strings.Contains(strings.ToLower(err.Error()), "not found") {
+		if storage.IsNotFoundError(err) {
+			log.Warn("LLM provider configuration not found",
+				slog.String("handle", id),
+				slog.Any("error", err))
+			c.JSON(http.StatusNotFound, api.ErrorResponse{
+				Status:  "error",
+				Message: fmt.Sprintf("LLM provider configuration with handle '%s' not found", id),
+			})
+			return
+		}
+		if !strings.Contains(strings.ToLower(err.Error()), "not found") {
 			log.Error("Failed to look up LLM provider", slog.Any("error", err))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go` around
lines 141 - 158, The two handlers (GetLLMProviderById and DeleteLLMProvider) use
different "not found" detection—one mixes storage.IsNotFoundError(err) with
strings.Contains(err.Error(), "not found") while the other only uses string
matching; standardize to the type-safe check by replacing any
strings.Contains(... "not found") usage with storage.IsNotFoundError(err) in
both handlers (look for the error handling blocks in GetLLMProviderById and
DeleteLLMProvider), remove the redundant string-based check, and adjust the
conditional branches/logging to rely solely on storage.IsNotFoundError(err) for
deciding between 404 and 500 responses.
gateway/gateway-controller/pkg/api/handlers/subscription_handler.go (1)

66-77: Conflated error and nil-plan check may mask actual errors.

Line 68 combines err != nil || plan == nil in one condition with identical handling. If GetSubscriptionPlanByID returns an actual error (e.g., database connection issue), it's logged as "plan not found" rather than surfacing the underlying error.

♻️ Suggested fix
 	if req.SubscriptionPlanId != nil && *req.SubscriptionPlanId != "" {
 		plan, err := s.db.GetSubscriptionPlanByID(*req.SubscriptionPlanId, "")
-		if err != nil || plan == nil {
-			log.Warn("Subscription plan not found for subscription creation",
-				slog.String("subscription_plan_id", *req.SubscriptionPlanId),
-				slog.String("api_id", apiID))
+		if err != nil {
+			if storage.IsNotFoundError(err) {
+				log.Warn("Subscription plan not found", slog.String("subscription_plan_id", *req.SubscriptionPlanId))
+			} else {
+				log.Error("Failed to get subscription plan", slog.String("subscription_plan_id", *req.SubscriptionPlanId), slog.Any("error", err))
+			}
+			c.JSON(http.StatusBadRequest, api.ErrorResponse{
+				Status:  "error",
+				Message: "Subscription plan not found or not enabled",
+			})
+			return
+		}
+		if plan == nil {
+			log.Warn("Subscription plan not found", slog.String("subscription_plan_id", *req.SubscriptionPlanId))
 			c.JSON(http.StatusBadRequest, api.ErrorResponse{
 				Status:  "error",
 				Message: "Subscription plan not found or not enabled",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/subscription_handler.go` around
lines 66 - 77, Separate the error and nil-plan checks when calling
s.db.GetSubscriptionPlanByID in the subscription creation flow: if err != nil,
log the actual error (use log.Error or similar and include err,
subscription_plan_id and apiID) and respond with an internal server error (500);
else if plan == nil, keep the current warning (log.Warn with
subscription_plan_id and apiID) and return the existing bad request response
(400) indicating "Subscription plan not found or not enabled". Update the branch
around s.db.GetSubscriptionPlanByID, req.SubscriptionPlanId, log.Warn/log.Error
and the c.JSON responses accordingly so real DB/errors are not masked as "plan
not found."
gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go (1)

151-163: Silently ignored json.Marshal error (same pattern as LLM provider handler).

Similar to llm_provider_handler.go, the json.Marshal error is discarded on line 152. Consider handling it consistently across handlers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go` around
lines 151 - 163, The json.Marshal error for cfg.SourceConfiguration is being
ignored; update the block around json.Marshal/json.Unmarshal so you check the
error returned by json.Marshal(cfg.SourceConfiguration) and handle it the same
way as the Unmarshal error: log the error via s.logger.Error (include the error
and identifying fields like cfg.UUID and cfg.DisplayName), return an HTTP 500
JSON error response via c.JSON with an appropriate message, and return early;
keep the existing json.Unmarshal handling for mcp unchanged (refer to
variables/functions json.Marshal, json.Unmarshal, mcp, cfg.SourceConfiguration,
s.logger.Error, and c.JSON).
gateway/gateway-controller/pkg/api/handlers/subscription_plan_handler.go (1)

77-81: Variable shadowing: s shadows the receiver.

Line 79 declares s := string(*req.ThrottleLimitUnit), which shadows the *APIServer receiver s. This is a code smell that could cause confusion or bugs if the receiver is accidentally used within this scope.

♻️ Suggested fix
 	var unitStr *string
 	if req.ThrottleLimitUnit != nil {
-		s := string(*req.ThrottleLimitUnit)
-		unitStr = &s
+		u := string(*req.ThrottleLimitUnit)
+		unitStr = &u
 	}

The same shadowing occurs at lines 112, 203, and 226—consider renaming all instances.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/subscription_plan_handler.go`
around lines 77 - 81, The code is shadowing the method receiver named s by
declaring local variables like s := string(*req.ThrottleLimitUnit); rename those
locals (e.g., unitVal or throttleUnitStr) and assign using val := string(...)
then unitStr = &val (or use var unitVal = string(...); unitStr = &unitVal) to
avoid shadowing the receiver; apply the same renaming pattern to the other
occurrences (the locals at the positions corresponding to lines ~112, ~203, and
~226) in subscription_plan_handler.go so no local variable reuses the receiver
name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gateway/gateway-controller/pkg/api/handlers/api_key_handler.go`:
- Around line 92-96: Several handlers in api_key_handler.go are returning raw
backend error strings (err.Error()) in 500 responses; instead, log the detailed
error and return a stable generic 500 message. For each place that does
c.JSON(http.StatusInternalServerError, api.ErrorResponse{Status: "error",
Message: err.Error()}) (occurring around the blocks at 92-96, 148-151, 239-242,
309-312, 362-365), replace the response to use a fixed message like "internal
server error" (or "failed to process request") while calling the existing logger
(or fmt/log) to record the full err with context; keep api.ErrorResponse for
structure but do not include err.Error() in Message. Ensure this change is
applied consistently across the handler functions in this file so internal
errors are logged but not leaked to clients.

In
`@gateway/gateway-controller/pkg/api/handlers/llm_provider_template_handler.go`:
- Around line 57-63: The handler currently maps all errors (e.g., in the
template parsing and CRUD branches that call log.Error and c.JSON with 400/404)
to client errors; instead, change error handling in the template handler
functions (where you call log.Error, c.JSON and return after parsing/CRUD
failures) to distinguish validation and not-found errors from internal failures:
use errors.Is/As against your domain errors (e.g., api.ErrNotFound, validation
error types) or explicit sentinel errors returned by the template service,
return 400 for validation failures and 404 for not-found, and for any other
error return a 500 with a generic message while logging the full error (keep the
existing slog.Any/ log.Error usage for internal errors); apply this change to
all similar branches (the parse branch and the create/get/update/delete branches
referenced).

In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go`:
- Around line 101-110: The current error handling in the LLM proxy creation
paths (e.g., the block using utils.IsPolicyDefinitionMissingError and returning
err.Error()) exposes internal error details and maps many backend failures to
400; change these handlers to explicitly detect and return client-safe 4xx
responses only for known validation/not-found/conflict cases (e.g.,
utils.IsPolicyDefinitionMissingError -> return the existing
PolicyDefinitionMissingUserMessage with 4xx), and for all other errors log the
full error with log.Error (keep slog.Any("error", err)) but return a stable
generic 500 response message (do not include err.Error()) to the client via
c.JSON. Apply the same pattern to the other similar handlers noted (the other
blocks around the listed ranges) so only explicit client-error helpers produce
4xx and all unexpected/internal failures produce a logged 500 with a generic
user message.

In `@gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go`:
- Around line 287-295: In UpdateMCPProxy, don't map every error to 404: inspect
the returned error from the lookup (use errors.Is/err == store.ErrNotFound or
your repository's sentinel error) and only return http.StatusNotFound for that
case; for validation errors return http.StatusBadRequest, and for unexpected
DB/internal errors return http.StatusInternalServerError. Update the log call
(log.Warn/log.Error) to include the actual error (err) and context (handle) and
use c.JSON with api.ErrorResponse using the appropriate HTTP status depending on
the error kind.

In `@gateway/gateway-controller/pkg/api/handlers/rest_api_handler.go`:
- Around line 76-85: The current error branch after h.parser.Parse(...)
incorrectly increments metrics.ValidationErrorsTotal with the "read_body_failed"
label (and the same issue around lines 183-192); change those increments to a
parse-specific label (e.g., "parse_body_failed" or "invalid_payload") so the
metric reflects parser/malformed payload errors rather than transport/read
failures—update the metrics.ValidationErrorsTotal.WithLabelValues(operation,
"<new_parse_label>").Inc() calls in the handler where h.parser.Parse is used
(and the analogous second branch) to use the new parse-specific label.

---

Nitpick comments:
In @.github/workflows/sample-service-pr-check.yml:
- Line 23: Update the cache key configuration so workspace files are included:
modify the cache-dependency-path entry (symbol: cache-dependency-path) to
include go.work and go.work.sum in addition to **/go.sum, ensuring changes in
the repository-level workspace files will invalidate the cache and avoid stale
cache hits for workspace members like samples/sample-service.

In `@gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go`:
- Around line 238-254: The handler currently logs a warning before classifying
the error from llmDeploymentService.DeleteLLMProvider; update the flow to
classify the error first (check strings.Contains(err.Error(), "not found")) and
then log at the appropriate level: use log.Warn (or Info) including context for
"not found" cases and log.Error (including the error details and
correlationID/handle) for other errors before returning the corresponding HTTP
response in the DeleteLLMProvider handler.
- Around line 50-57: The json.Marshal error is being ignored for
cfg.SourceConfiguration; change the code to capture and handle that error before
proceeding to json.Unmarshal: replace the ignored marshal with j, err :=
json.Marshal(cfg.SourceConfiguration) and if err != nil log the failure (use the
same logger/slog context as the existing unmarshalling error, include cfg.UUID
and the err), return an HTTP 500 via c.JSON with an appropriate ErrorResponse,
and only call json.Unmarshal(&prov) when the marshal succeeds.
- Around line 141-158: The two handlers (GetLLMProviderById and
DeleteLLMProvider) use different "not found" detection—one mixes
storage.IsNotFoundError(err) with strings.Contains(err.Error(), "not found")
while the other only uses string matching; standardize to the type-safe check by
replacing any strings.Contains(... "not found") usage with
storage.IsNotFoundError(err) in both handlers (look for the error handling
blocks in GetLLMProviderById and DeleteLLMProvider), remove the redundant
string-based check, and adjust the conditional branches/logging to rely solely
on storage.IsNotFoundError(err) for deciding between 404 and 500 responses.

In `@gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go`:
- Around line 151-163: The json.Marshal error for cfg.SourceConfiguration is
being ignored; update the block around json.Marshal/json.Unmarshal so you check
the error returned by json.Marshal(cfg.SourceConfiguration) and handle it the
same way as the Unmarshal error: log the error via s.logger.Error (include the
error and identifying fields like cfg.UUID and cfg.DisplayName), return an HTTP
500 JSON error response via c.JSON with an appropriate message, and return
early; keep the existing json.Unmarshal handling for mcp unchanged (refer to
variables/functions json.Marshal, json.Unmarshal, mcp, cfg.SourceConfiguration,
s.logger.Error, and c.JSON).

In `@gateway/gateway-controller/pkg/api/handlers/secret_handler.go`:
- Around line 91-93: ListSecrets, GetSecret, and DeleteSecret use s.logger
directly while CreateSecret and UpdateSecret use middleware.GetLogger(c,
s.logger); update the former three to obtain a request-scoped logger by calling
middleware.GetLogger(c, s.logger) at the start of each handler (e.g., in
ListSecrets, GetSecret, DeleteSecret) and replace uses of s.logger in those
functions with the returned log variable so all handlers consistently include
request context.
- Around line 368-369: The DELETE handler DeleteSecret currently responds with
c.Status(http.StatusOK) and no body; change it to return a JSON response
consistent with other handlers (use c.JSON(http.StatusOK, ...) ) containing a
clear status and identifying info (e.g., {"status":"ok","message":"secret
deleted","id": <secretID>} or similar), retrieving the secret ID from the
request/context the same way DeleteSecret currently uses; ensure the response
shape matches existing delete handlers for consistency.
- Around line 67-75: The log message in the CreateSecret handler is
misleading—replace the specific "Failed to encrypt Secret" message with a more
accurate, generic message such as "Failed to create secret" (while still logging
the error with slog.Any) in the error handling block that uses
storage.IsConflictError and c.JSON to return the response; update the slog.Error
call so it reads a generic "Failed to create secret" and retains
slog.Any("error", err) for context.

In `@gateway/gateway-controller/pkg/api/handlers/subscription_handler.go`:
- Around line 66-77: Separate the error and nil-plan checks when calling
s.db.GetSubscriptionPlanByID in the subscription creation flow: if err != nil,
log the actual error (use log.Error or similar and include err,
subscription_plan_id and apiID) and respond with an internal server error (500);
else if plan == nil, keep the current warning (log.Warn with
subscription_plan_id and apiID) and return the existing bad request response
(400) indicating "Subscription plan not found or not enabled". Update the branch
around s.db.GetSubscriptionPlanByID, req.SubscriptionPlanId, log.Warn/log.Error
and the c.JSON responses accordingly so real DB/errors are not masked as "plan
not found."

In `@gateway/gateway-controller/pkg/api/handlers/subscription_plan_handler.go`:
- Around line 77-81: The code is shadowing the method receiver named s by
declaring local variables like s := string(*req.ThrottleLimitUnit); rename those
locals (e.g., unitVal or throttleUnitStr) and assign using val := string(...)
then unitStr = &val (or use var unitVal = string(...); unitStr = &unitVal) to
avoid shadowing the receiver; apply the same renaming pattern to the other
occurrences (the locals at the positions corresponding to lines ~112, ~203, and
~226) in subscription_plan_handler.go so no local variable reuses the receiver
name.

In `@gateway/gateway-controller/pkg/api/handlers/websub_api_handler.go`:
- Around line 30-71: Multiple handlers (CreateWebSubAPI, ListWebSubAPIs,
GetWebSubAPIById, UpdateWebSubAPI, DeleteWebSubAPI) return the same 501 JSON
payload repeatedly; extract a shared constant for the status/message and a small
helper method (e.g., writeNotImplemented(c *gin.Context) or
s.writeNotImplemented) that sends c.JSON(http.StatusNotImplemented,
api.ErrorResponse{...}) and replace each handler body to call that helper to
avoid duplication and reduce drift risk.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 97b4deab-ba8a-4ecb-962c-bed1ac8fa4cf

📥 Commits

Reviewing files that changed from the base of the PR and between 93b13b6 and f3b9027.

📒 Files selected for processing (28)
  • .github/workflows/cli-gw-build-periodic.yml
  • .github/workflows/cli-release.yml
  • .github/workflows/codecov.yml
  • .github/workflows/gateway-integration-test-postgres.yml
  • .github/workflows/gateway-integration-test.yml
  • .github/workflows/gateway-release.yml
  • .github/workflows/go-scan.yaml
  • .github/workflows/operator-integration-test.yml
  • .github/workflows/platform-api-pr-check.yml
  • .github/workflows/platform-api-release.yml
  • .github/workflows/sample-service-pr-check.yml
  • .github/workflows/sample-service-release.yml
  • .github/workflows/sdk-integration-test.yml
  • gateway/gateway-controller/pkg/api/handlers/api_key_handler.go
  • gateway/gateway-controller/pkg/api/handlers/handlers.go
  • gateway/gateway-controller/pkg/api/handlers/handlers_test.go
  • gateway/gateway-controller/pkg/api/handlers/llm_provider_handler.go
  • gateway/gateway-controller/pkg/api/handlers/llm_provider_template_handler.go
  • gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go
  • gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go
  • gateway/gateway-controller/pkg/api/handlers/rest_api_handler.go
  • gateway/gateway-controller/pkg/api/handlers/secret_handler.go
  • gateway/gateway-controller/pkg/api/handlers/subscription_handler.go
  • gateway/gateway-controller/pkg/api/handlers/subscription_plan_handler.go
  • gateway/gateway-controller/pkg/api/handlers/websub_api_handler.go
  • gateway/gateway-controller/pkg/service/errors.go
  • gateway/gateway-controller/pkg/service/restapi_service.go
  • gateway/gateway-controller/pkg/service/restapi_service_impl.go

Comment thread gateway/gateway-controller/pkg/api/handlers/api_key_handler.go
Comment thread gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go Outdated
Comment thread gateway/gateway-controller/pkg/api/handlers/mcp_proxy_handler.go
Comment thread gateway/gateway-controller/pkg/api/handlers/rest_api_handler.go Outdated
Distinguish 4xx vs 5xx errors by sentinel type in the LLM proxy,
template, and API key handlers. Prevents internal error details from
leaking in 500 responses and fixes misclassified errors (e.g. not-found
returned as 400, DB failures returned as 400/404).

- Add ErrLLMProxyValidation, ErrLLMTemplateValidation, and
  ErrLLMTemplateNotFound sentinels; wrap service errors with %w so
  handlers can use errors.Is/storage.Is* helpers
- Fix DeployLLMProxyConfiguration, UpdateLLMProxy, DeleteLLMProxy to
  propagate storage.ErrNotFound and ErrLLMProxyValidation
- Fix api_key.go not-found returns to wrap storage.ErrNotFound with %w
- Handlers now return 400 for validation, 404 for not-found, 409 for
  conflict, 500 (generic message + log.Error) for all other failures
Copy link
Copy Markdown
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
gateway/gateway-controller/pkg/utils/llm_deployment.go (1)

1101-1125: ⚠️ Potential issue | 🟠 Major

Malformed proxy updates still miss the new validation sentinel.

UpdateLLMProxy parses the body in isLLMProxyUndeployRequest before it reaches DeployLLMProxyConfiguration. That helper still returns a plain parse error, so gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go falls through to 500 instead of the new 400 validation path on bad PUT payloads.

🩹 Suggested fix

Apply the same sentinel in the pre-parse helper:

 func (s *LLMDeploymentService) isLLMProxyUndeployRequest(params LLMDeploymentParams) (bool, error) {
 	var proxyConfig api.LLMProxyConfiguration
 	if err := s.parser.Parse(params.Data, params.ContentType, &proxyConfig); err != nil {
-		return false, fmt.Errorf("failed to parse proxy configuration: %w", err)
+		return false, fmt.Errorf("%w: failed to parse proxy configuration: %v", ErrLLMProxyValidation, err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/utils/llm_deployment.go` around lines 1101 -
1125, The pre-parse helper isLLMProxyUndeployRequest is returning raw parse
errors so UpdateLLMProxy surfaces a 500 instead of the intended 400 validation
error; modify isLLMProxyUndeployRequest to apply the same validation sentinel
used in DeployLLMProxyConfiguration (wrap or convert JSON/body parse errors into
the validation sentinel type/error) so malformed PUT payloads produce the
validation 400 path; ensure UpdateLLMProxy still calls isLLMProxyUndeployRequest
and that DeployLLMProxyConfiguration continues to receive validated params.
♻️ Duplicate comments (2)
gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go (2)

214-221: ⚠️ Potential issue | 🟠 Major

Map update conflicts to 409 as well.

UpdateLLMProxy goes through the same deployment/conflict checks as create. If the service returns storage.ErrConflict, this block currently falls through to 500.

🩹 Suggested fix
 	if errors.Is(err, utils.ErrLLMProxyValidation) {
 		log.Warn("LLM proxy configuration invalid", slog.Any("error", err))
 		c.JSON(http.StatusBadRequest, api.ErrorResponse{Status: "error", Message: err.Error()})
 		return
 	}
+	if storage.IsConflictError(err) {
+		c.JSON(http.StatusConflict, api.ErrorResponse{Status: "error", Message: err.Error()})
+		return
+	}
 	log.Error("Failed to update LLM proxy configuration", slog.String("id", id), slog.Any("error", err))

Also applies to: 229-237

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go` around
lines 214 - 221, The error handling in UpdateLLMProxy currently maps only
storage.IsNotFoundError to 404 and otherwise returns 500; update the handler to
also detect storage.ErrConflict (or via a helper like storage.IsConflictError)
and return HTTP 409 with a clear conflict message. Locate the error handling
branches around the existing storage.IsNotFoundError check in UpdateLLMProxy
(and the similar block at the second occurrence) and add a conditional branch
that checks for storage.ErrConflict and responds with
c.JSON(http.StatusConflict, api.ErrorResponse{Status:"error", Message:
fmt.Sprintf("LLM proxy configuration with handle '%s' conflict", id)}) before
falling through to the generic 500 case.

102-109: ⚠️ Potential issue | 🟠 Major

Keep unknown policy references on the 4xx path.

When IsPolicyDefinitionMissingError is true, the submitted proxy config is invalid from the client's perspective. Returning 500 here still reports a client-correctable payload problem as a server fault.

🩹 Suggested fix

Use the same friendly message, but keep the status code in the client-error range in both branches:

- c.JSON(http.StatusInternalServerError, api.ErrorResponse{
+ c.JSON(http.StatusBadRequest, api.ErrorResponse{
 	Status:  "error",
 	Message: utils.PolicyDefinitionMissingUserMessage,
 })

Also applies to: 222-228

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go` around
lines 102 - 109, The handler currently treats
utils.IsPolicyDefinitionMissingError as a server error and returns
http.StatusInternalServerError; instead return a 4xx client error (e.g.,
http.StatusBadRequest or http.StatusUnprocessableEntity) while keeping the same
friendly message (utils.PolicyDefinitionMissingUserMessage). Update the c.JSON
call in the IsPolicyDefinitionMissingError branch (where log.Error is called) to
use a 4xx status code, and apply the same change to the other occurrence around
lines 222-228 so any invalid/unknown policy references return a client-error
status rather than 500.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gateway/gateway-controller/pkg/utils/llm_deployment.go`:
- Around line 746-749: The code currently converts any non-nil error returned by
s.GetLLMProviderTemplateByHandle into ErrLLMTemplateNotFound which hides real
DB/store errors; update the lookup handling in the function (and the similar
block used in DeleteLLMProviderTemplate) to inspect the returned error: if it is
a not-found sentinel from the store return ErrLLMTemplateNotFound (wrapping as
needed), otherwise return or wrap the underlying error (preserving the original
error) so backend failures surface as 5xx instead of 404.

---

Outside diff comments:
In `@gateway/gateway-controller/pkg/utils/llm_deployment.go`:
- Around line 1101-1125: The pre-parse helper isLLMProxyUndeployRequest is
returning raw parse errors so UpdateLLMProxy surfaces a 500 instead of the
intended 400 validation error; modify isLLMProxyUndeployRequest to apply the
same validation sentinel used in DeployLLMProxyConfiguration (wrap or convert
JSON/body parse errors into the validation sentinel type/error) so malformed PUT
payloads produce the validation 400 path; ensure UpdateLLMProxy still calls
isLLMProxyUndeployRequest and that DeployLLMProxyConfiguration continues to
receive validated params.

---

Duplicate comments:
In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go`:
- Around line 214-221: The error handling in UpdateLLMProxy currently maps only
storage.IsNotFoundError to 404 and otherwise returns 500; update the handler to
also detect storage.ErrConflict (or via a helper like storage.IsConflictError)
and return HTTP 409 with a clear conflict message. Locate the error handling
branches around the existing storage.IsNotFoundError check in UpdateLLMProxy
(and the similar block at the second occurrence) and add a conditional branch
that checks for storage.ErrConflict and responds with
c.JSON(http.StatusConflict, api.ErrorResponse{Status:"error", Message:
fmt.Sprintf("LLM proxy configuration with handle '%s' conflict", id)}) before
falling through to the generic 500 case.
- Around line 102-109: The handler currently treats
utils.IsPolicyDefinitionMissingError as a server error and returns
http.StatusInternalServerError; instead return a 4xx client error (e.g.,
http.StatusBadRequest or http.StatusUnprocessableEntity) while keeping the same
friendly message (utils.PolicyDefinitionMissingUserMessage). Update the c.JSON
call in the IsPolicyDefinitionMissingError branch (where log.Error is called) to
use a 4xx status code, and apply the same change to the other occurrence around
lines 222-228 so any invalid/unknown policy references return a client-error
status rather than 500.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: ce722b7a-d494-4a3d-964c-41b0cc1a6bd1

📥 Commits

Reviewing files that changed from the base of the PR and between f3b9027 and 3163125.

📒 Files selected for processing (5)
  • gateway/gateway-controller/pkg/api/handlers/api_key_handler.go
  • gateway/gateway-controller/pkg/api/handlers/llm_provider_template_handler.go
  • gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go
  • gateway/gateway-controller/pkg/utils/api_key.go
  • gateway/gateway-controller/pkg/utils/llm_deployment.go
✅ Files skipped from review due to trivial changes (1)
  • gateway/gateway-controller/pkg/api/handlers/api_key_handler.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • gateway/gateway-controller/pkg/api/handlers/llm_provider_template_handler.go

Comment thread gateway/gateway-controller/pkg/utils/llm_deployment.go
Copy link
Copy Markdown
Contributor

@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)
gateway/gateway-controller/pkg/utils/llm_deployment.go (1)

379-379: Consider preserving the full error chain with %w.

Using %v for the inner error loses its type information in the error chain. While current handlers only check for the sentinel via errors.Is, preserving the chain allows future callers to inspect the underlying parse error.

♻️ Suggested improvement
-		return nil, fmt.Errorf("%w: failed to parse proxy configuration: %v", ErrLLMProxyValidation, err)
+		return nil, fmt.Errorf("%w: failed to parse proxy configuration: %w", ErrLLMProxyValidation, err)

Similarly at line 533:

-		return nil, fmt.Errorf("%w: failed to parse template configuration: %v", ErrLLMTemplateValidation, err)
+		return nil, fmt.Errorf("%w: failed to parse template configuration: %w", ErrLLMTemplateValidation, err)

Also applies to: 533-533

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/utils/llm_deployment.go` at line 379, The
error construction is losing the wrapped error by using "%v"; update the
fmt.Errorf calls that return ErrLLMProxyValidation (the instance referenced as
ErrLLMProxyValidation in llm_deployment.go) to wrap the inner parse error with
"%w" so the original error chain is preserved (apply this change at the
occurrences noted around the current return at line 379 and the similar
occurrence near line 533), ensuring callers can use errors.Unwrap / errors.Is to
inspect the underlying parse error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@gateway/gateway-controller/pkg/utils/llm_deployment.go`:
- Line 379: The error construction is losing the wrapped error by using "%v";
update the fmt.Errorf calls that return ErrLLMProxyValidation (the instance
referenced as ErrLLMProxyValidation in llm_deployment.go) to wrap the inner
parse error with "%w" so the original error chain is preserved (apply this
change at the occurrences noted around the current return at line 379 and the
similar occurrence near line 533), ensuring callers can use errors.Unwrap /
errors.Is to inspect the underlying parse error.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2638f006-903b-406b-a244-bb9aba296feb

📥 Commits

Reviewing files that changed from the base of the PR and between 3163125 and ae436a7.

📒 Files selected for processing (1)
  • gateway/gateway-controller/pkg/utils/llm_deployment.go

Copy link
Copy Markdown
Contributor

@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

♻️ Duplicate comments (1)
gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go (1)

103-109: ⚠️ Potential issue | 🟠 Major

Return a client error for missing policy definitions, not 500.

Line 103 and Line 229 classify utils.IsPolicyDefinitionMissingError(err) as internal server error. This is a request-side semantic failure and should be returned as 4xx (typically 400) with the same safe message.

Proposed patch
-			c.JSON(http.StatusInternalServerError, api.ErrorResponse{
+			c.JSON(http.StatusBadRequest, api.ErrorResponse{
 				Status:  "error",
 				Message: utils.PolicyDefinitionMissingUserMessage,
 			})

Apply in both CreateLLMProxy and UpdateLLMProxy.

Also applies to: 229-235

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go` around
lines 103 - 109, The handler treats utils.IsPolicyDefinitionMissingError as a
500 but it should be a client error; update both CreateLLMProxy and
UpdateLLMProxy handlers to return HTTP 400 (Bad Request) instead of
http.StatusInternalServerError when utils.IsPolicyDefinitionMissingError(err) is
true, keeping the same api.ErrorResponse.Message
(utils.PolicyDefinitionMissingUserMessage) and log call; ensure the change is
applied in both the error branches that currently call
c.JSON(http.StatusInternalServerError, ...) for that check.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go`:
- Around line 156-182: GetLLMProxyByHandle may return (nil, nil) so add a
nil-check on cfg immediately after the call in the handler (the block that
builds proxyDetail and accesses cfg.SourceConfiguration, cfg.DesiredState,
cfg.CreatedAt, cfg.UpdatedAt); if cfg == nil return a 404 JSON error (like the
storage.IsNotFoundError branch) with a clear message and log.Warn including the
handle, then return to avoid dereferencing nil.
- Around line 51-53: The code currently discards the error from json.Marshal
when converting cfg.SourceConfiguration (j, _ :=
json.Marshal(cfg.SourceConfiguration)) and always treats failures as
json.Unmarshal errors; change this to capture and check the marshal error before
attempting to unmarshal (e.g., j, err := json.Marshal(cfg.SourceConfiguration)),
and if marshal fails log a clear error referencing cfg.UUID and return/skip
processing rather than proceeding to json.Unmarshal into proxy; update the log
messages in the llm_proxy_handler.go handler where json.Marshal and
json.Unmarshal are used so marshal and unmarshal errors are reported separately
and accurately.

---

Duplicate comments:
In `@gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go`:
- Around line 103-109: The handler treats utils.IsPolicyDefinitionMissingError
as a 500 but it should be a client error; update both CreateLLMProxy and
UpdateLLMProxy handlers to return HTTP 400 (Bad Request) instead of
http.StatusInternalServerError when utils.IsPolicyDefinitionMissingError(err) is
true, keeping the same api.ErrorResponse.Message
(utils.PolicyDefinitionMissingUserMessage) and log call; ensure the change is
applied in both the error branches that currently call
c.JSON(http.StatusInternalServerError, ...) for that check.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 739dfb01-d6f1-4913-ab5f-231cfc84d26b

📥 Commits

Reviewing files that changed from the base of the PR and between ae436a7 and 7a642de.

📒 Files selected for processing (5)
  • gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go
  • gateway/gateway-controller/pkg/utils/llm_deployment.go
  • gateway/gateway-controller/pkg/utils/llm_transformer.go
  • gateway/it/features/llm-provider-templates.feature
  • gateway/it/features/llm-proxies.feature
✅ Files skipped from review due to trivial changes (3)
  • gateway/it/features/llm-provider-templates.feature
  • gateway/it/features/llm-proxies.feature
  • gateway/gateway-controller/pkg/utils/llm_transformer.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • gateway/gateway-controller/pkg/utils/llm_deployment.go

Comment thread gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go
Comment thread gateway/gateway-controller/pkg/api/handlers/llm_proxy_handler.go
@renuka-fernando renuka-fernando merged commit 69b9198 into wso2:main Apr 8, 2026
8 of 9 checks passed
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.

2 participants