Skip to content

Add event for API deletion #1079

Open
dushaniw wants to merge 9 commits intowso2:mainfrom
dushaniw:main
Open

Add event for API deletion #1079
dushaniw wants to merge 9 commits intowso2:mainfrom
dushaniw:main

Conversation

@dushaniw
Copy link
Contributor

@dushaniw dushaniw commented Feb 11, 2026

Purpose

Explain why this feature or fix is required. Describe the underlying problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc.

Goals

Describe what solutions this feature or fix introduces to address the problems outlined above.

Approach

Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI. Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here.

User stories

Summary of user stories addressed by this change>

Documentation

Link(s) to product documentation that addresses the changes of this PR. If no doc impact, enter “N/A” plus brief explanation of why there’s no doc impact

Automation tests

  • Unit tests

    Code coverage information

  • Integration tests

    Details about the test cases and coverage

Security checks

Samples

Provide high-level details about the samples related to this feature

Related PRs

List any other related PRs

Test environment

List all JDK versions, operating systems, databases, and browser/versions on which this feature/fix was tested

Summary by CodeRabbit

  • New Features

    • Gateways receive API deletion notifications (includes vhost) with per-gateway delivery reporting.
    • Broadcast support added for API deletion events alongside deploy/undeploy.
  • Improvements

    • Deployment payloads standardized to "deploymentId" (replaces "revisionId").
    • Better orphaned-resource cleanup, policy/xDS update handling, and more robust config lookup.
  • Tests

    • Extensive deletion and deletion-error-path tests added to validate workflows and storage fallbacks.

Copilot AI review requested due to automatic review settings February 11, 2026 10:51
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 11, 2026

Walkthrough

Adds API deletion events/handling and broadcasts, renames deployment identifier RevisionID → DeploymentID across gateway and platform, injects an apiKeyXDSManager into the control-plane client, and implements deletion/cleanup pipelines (DB, in-memory store, policy, xDS) with comprehensive tests.

Changes

Cohort / File(s) Summary
Event Types & DTOs
gateway/gateway-controller/pkg/controlplane/events.go, platform-api/src/internal/dto/gateway_event.go, platform-api/src/internal/model/gateway_event.go
Renamed deployment field RevisionIDDeploymentID (JSON key deploymentId). Added new API deletion payload/DTO types (APIDeletedEvent / APIDeletionEvent) carrying apiId, vhost, and environment.
Control Plane Client
gateway/gateway-controller/pkg/controlplane/client.go
Added apiKeyXDSManager field and parameter in NewClient; implemented api.deleted message handling and helpers for full API deletion and orphan cleanup (DB removal, in-memory config/key cleanup, policy cleanup, async xDS snapshot updates).
Client Tests & Mocks
gateway/gateway-controller/pkg/controlplane/api_deleted_test.go, gateway/gateway-controller/pkg/controlplane/controlplane_test.go, gateway/gateway-controller/pkg/controlplane/client_integration_test.go, gateway/gateway-controller/pkg/api/handlers/handlers_test.go, gateway/gateway-controller/pkg/utils/api_utils_test.go
Updated tests to use DeploymentID; added a large test suite for API deletion handling including mock storage, orphan cleanup, full deletion, memory-only flows, and error paths; minor test comment/name tweaks.
Platform API — Services
platform-api/src/internal/service/api.go, platform-api/src/internal/service/deployment.go, platform-api/src/internal/service/gateway_events.go
Emitters now populate DeploymentID; platform service fetches gateway associations and broadcasts "api.deleted" events (includes vhost lookup). Added BroadcastAPIDeletionEvent to GatewayEventsService mirroring existing broadcast flow.
Storage & Memory
gateway/gateway-controller/pkg/storage/memory.go
Standardized not-found error returns to wrapped ErrNotFound in in-memory storage lookup paths.
Misc / Manifest
go.mod
Module/manifest updates included as part of changes.

Sequence Diagram(s)

sequenceDiagram
    participant ControlPlane as Control Plane
    participant Client as Control Plane Client
    participant DB as Database
    participant Store as Config Store
    participant Policy as Policy Engine
    participant XDS as XDS Manager

    ControlPlane->>Client: WebSocket "api.deleted" (apiId, environment, vhost)
    Client->>DB: findAPIConfig(apiId, environment)
    DB-->>Client: config or not found
    alt API found
        Client->>DB: performFullAPIDeletion(apiId)
        Client->>Store: remove in-memory config & purge API keys
        Client->>Policy: remove derived policies
        Client->>XDS: updateXDSSnapshotAsync()
    else Orphan / not found
        Client->>Store: cleanupOrphanedResources(apiId)
        Client->>XDS: conditional snapshot update
    end
    Client-->>ControlPlane: log/report deletion outcome
Loading
sequenceDiagram
    participant APIService as Platform API Service
    participant GatewayEvents as GatewayEventsService
    participant GatewayStore as Gateway connection store

    APIService->>GatewayEvents: BroadcastAPIDeletionEvent(gatewayID, APIDeletionEvent)
    GatewayEvents->>GatewayEvents: Generate correlationId, serialize payload
    GatewayEvents->>GatewayStore: Get active connections for gatewayID
    GatewayStore-->>GatewayEvents: Return connections
    loop per connection
        GatewayEvents->>Connection: Send event (type: "api.deleted")
        Connection-->>GatewayEvents: Ack / Error
    end
    GatewayEvents-->>APIService: Aggregated delivery result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through logs where deletions bloom,

DeploymentID bounced, clearing keys from the room,
I nudged the policies, swept orphans away,
Snapshots hummed softly, gateways heard me say,
A tidy hop — the system’s neat today.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely a template with unfilled placeholders; no substantive content provided for any required sections including Purpose, Goals, Approach, Documentation, Tests, or Security checks. Fill in all template sections with concrete details: explain why API deletion event is needed, describe implementation approach, provide test coverage details, confirm security checks, and include relevant documentation links.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add event for API deletion' accurately summarizes the main changes: introducing new event types (APIDeletedEvent, APIDeletedEventPayload) and deletion workflows across the codebase.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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
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

🤖 Fix all issues with AI agents
In `@gateway/gateway-controller/pkg/controlplane/client.go`:
- Around line 876-899: The cleanup currently treats any error from
c.db.RemoveAPIKeysAPI and c.store.RemoveAPIKeysByAPI as "no API keys found" and
logs at Debug; update the error handling in the cleanup block so you distinguish
a not-found result from real errors (inspect the returned error or use the
package's sentinel/generic IsNotFound helper) and only log the not-found case as
Info/Debug, while logging genuine failures at Warn or Error and include the
error details (use c.logger.Warn/c.logger.Error with slog.String("api_id",
apiID) and slog.Any("err", err)); keep setting hasOrphanedResources=true only
when removal succeeded and ensure both c.db.RemoveAPIKeysAPI and
c.store.RemoveAPIKeysByAPI follow this corrected pattern.
🧹 Nitpick comments (4)
gateway/gateway-controller/pkg/api/handlers/handlers_test.go (1)

397-398: Reminder: rebuild gateway images after gateway changes.
Please run cd gateway && make build-local as part of this update. As per coding guidelines: “gateway/**/*.{go,yaml,yml,Dockerfile}: When modifying code in gateway components … rebuild Docker images using cd gateway && make build-local.”

platform-api/src/internal/service/api.go (1)

340-345: Hardcoded "production" environment — consistent but potentially fragile.

The environment is hardcoded to "production" here, matching the same pattern in deployment.go (lines 194, 262, 325). If multi-environment support is planned, this should eventually be derived from the deployment or gateway configuration. Not a blocker since it's consistent with existing code.

platform-api/src/internal/service/gateway_events.go (1)

211-282: Consider extracting a generic broadcast helper to reduce duplication.

All Broadcast*Event methods in this file share ~95% identical logic (correlationID generation, payload serialization, size validation, DTO creation, connection iteration, stats tracking, summary logging). A generic helper parameterized by event type string and payload would eliminate this repeated boilerplate. Not urgent given the existing pattern, but the surface area keeps growing with each new event type.

gateway/gateway-controller/pkg/controlplane/client.go (1)

804-1083: Consider extracting orphan cleanup and full cleanup into private helpers.

handleAPIDeletedEvent is ~280 lines with two distinct code paths (orphan cleanup vs. full cleanup). Extracting these into cleanupOrphanedResources(apiID, correlationID) and performFullAPIDeletion(apiID, apiConfig, correlationID) helpers would improve readability and testability.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an explicit api.deleted gateway event so gateways can proactively clean up API config/routes/keys when an API is permanently removed from the platform. This also standardizes deployment event payload naming by switching from revisionId to deploymentId across platform-api and gateway-controller.

Changes:

  • Add api.deleted event model + broadcaster in platform-api and emit it during API deletion.
  • Rename deployment event field revisionIddeploymentId across DTOs/models and gateway-controller consumers/tests.
  • Add gateway-controller handling for api.deleted to clean up stored configs, API keys, policy artifacts, and update snapshots.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
platform-api/src/internal/service/gateway_events.go Adds BroadcastAPIDeletionEvent to publish api.deleted over WebSocket.
platform-api/src/internal/service/deployment.go Updates deployment event payload to use DeploymentID instead of RevisionID.
platform-api/src/internal/service/api.go Fetches gateway associations pre-delete and broadcasts api.deleted to associated gateways.
platform-api/src/internal/model/gateway_event.go Extends gateway event model: adds APIDeletionEvent and renames RevisionIDDeploymentID.
platform-api/src/internal/dto/gateway_event.go Renames DTO field RevisionIDDeploymentID.
gateway/gateway-controller/pkg/utils/api_utils_test.go Comment-only update reflecting deployment terminology.
gateway/gateway-controller/pkg/controlplane/events.go Adds APIDeletedEvent structs and renames deployed payload field to DeploymentID.
gateway/gateway-controller/pkg/controlplane/controlplane_test.go Updates tests to validate DeploymentID field.
gateway/gateway-controller/pkg/controlplane/client_integration_test.go Updates deployed event JSON + adds api.deleted to “all event types” coverage.
gateway/gateway-controller/pkg/controlplane/client.go Handles api.deleted and performs cleanup + snapshot updates; adds apiKeyXDSManager to support policy-engine API key cleanup.
gateway/gateway-controller/pkg/api/handlers/handlers_test.go Updates mock signature parameter name (revisionIDdeploymentID).

Comment on lines +315 to +319
// Get all gateway associations BEFORE deletion (associations will be cascade deleted)
gatewayAssociations, err := s.apiRepo.GetAPIAssociations(apiUUID, constants.AssociationTypeGateway, orgUUID)
if err != nil {
log.Printf("[WARN] Failed to get gateway associations for API deletion: apiUUID=%s error=%v", apiUUID, err)
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

If GetAPIAssociations fails here, the API is still deleted but no gateway deletion events will be sent (and associations are cascade-deleted, so you can’t recover the list later). That can leave gateways serving stale routes/config for a deleted API. Consider treating this as a hard failure (return the error) or otherwise ensuring you can still notify all relevant gateways before proceeding with the repository delete.

Copilot uses AI. Check for mistakes.
Copy link
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: 3

🤖 Fix all issues with AI agents
In `@gateway/gateway-controller/pkg/controlplane/client.go`:
- Around line 889-918: The non-orphaned log messages in updateXDSSnapshotAsync
incorrectly say "API deletion" when the call can come from undeployment (via
handleAPIUndeployedEvent), so change the logging to reflect the actual
operation: add a small parameter (e.g., op string or isUndeploy bool) to
updateXDSSnapshotAsync or compute an operation label before logging and use a
generic message like "after API change" or the op label; update the error/info
branches to use that label (referencing updateXDSSnapshotAsync and the caller
handleAPIUndeployedEvent) so logs are accurate for both deletion and
undeployment contexts.
- Around line 816-831: findAPIConfig currently treats any db.GetConfig error as
"not found", which hides transient DB failures and causes handleAPIDeletedEvent
to take the orphan path; change findAPIConfig (and its callers) to surface DB
errors instead of folding them into a not-found result by updating the signature
to return (*models.StoredConfig, bool, error) or (*models.StoredConfig, error)
with a distinct not-found sentinel, have findAPIConfig return the actual db
error when c.db.GetConfig returns an error, and update handleAPIDeletedEvent to
check for and handle that error (retry/abort) before performing orphan cleanup
so real DB failures are not treated as missing configs.
- Around line 1077-1081: The current sequence calls
c.updateXDSSnapshotAsync(apiID, correlationID, false) before
c.removePolicyConfiguration(apiID, correlationID, false), which can let the
async snapshot include deleted policy; swap the two calls so
c.removePolicyConfiguration(apiID, correlationID, false) is invoked first, then
c.updateXDSSnapshotAsync(apiID, correlationID, false), ensuring policy removal
happens before the xDS snapshot computation (use the same apiID and
correlationID parameters).

Comment on lines +889 to +918
// updateXDSSnapshotAsync updates xDS snapshot in the background
func (c *Client) updateXDSSnapshotAsync(apiID, correlationID string, isOrphaned bool) {
if c.snapshotManager == nil {
return
}

go func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := c.snapshotManager.UpdateSnapshot(ctx, undeployedEvent.CorrelationID); err != nil {
c.logger.Error("Failed to update xDS snapshot after API undeployment",
if err := c.snapshotManager.UpdateSnapshot(ctx, correlationID); err != nil {
// Log level depends on operation context
if isOrphaned {
c.logger.Warn("Failed to update xDS snapshot for orphaned resource cleanup",
slog.String("api_id", apiID),
slog.Any("error", err),
)
} else {
c.logger.Error("Failed to update xDS snapshot after API deletion",
slog.String("api_id", apiID),
slog.Any("error", err),
)
}
} else if !isOrphaned {
c.logger.Info("Successfully updated xDS snapshot after API deletion",
slog.String("api_id", apiID),
)
}
}()
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Log messages are misleading when called from the undeployment path.

updateXDSSnapshotAsync is called from handleAPIUndeployedEvent (line 808) with isOrphaned=false, but the non-orphaned log messages at lines 907 and 913 reference "API deletion." This will produce confusing log entries during undeployment events.

Consider parameterizing the operation context or using a more generic message:

Suggested fix
-			} else {
-				c.logger.Error("Failed to update xDS snapshot after API deletion",
-					slog.String("api_id", apiID),
-					slog.Any("error", err),
-				)
-			}
-		} else if !isOrphaned {
-			c.logger.Info("Successfully updated xDS snapshot after API deletion",
-				slog.String("api_id", apiID),
-			)
+			} else {
+				c.logger.Error("Failed to update xDS snapshot",
+					slog.String("api_id", apiID),
+					slog.String("correlation_id", correlationID),
+					slog.Any("error", err),
+				)
+			}
+		} else if !isOrphaned {
+			c.logger.Info("Successfully updated xDS snapshot",
+				slog.String("api_id", apiID),
+				slog.String("correlation_id", correlationID),
+			)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// updateXDSSnapshotAsync updates xDS snapshot in the background
func (c *Client) updateXDSSnapshotAsync(apiID, correlationID string, isOrphaned bool) {
if c.snapshotManager == nil {
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := c.snapshotManager.UpdateSnapshot(ctx, undeployedEvent.CorrelationID); err != nil {
c.logger.Error("Failed to update xDS snapshot after API undeployment",
if err := c.snapshotManager.UpdateSnapshot(ctx, correlationID); err != nil {
// Log level depends on operation context
if isOrphaned {
c.logger.Warn("Failed to update xDS snapshot for orphaned resource cleanup",
slog.String("api_id", apiID),
slog.Any("error", err),
)
} else {
c.logger.Error("Failed to update xDS snapshot after API deletion",
slog.String("api_id", apiID),
slog.Any("error", err),
)
}
} else if !isOrphaned {
c.logger.Info("Successfully updated xDS snapshot after API deletion",
slog.String("api_id", apiID),
)
}
}()
}
// updateXDSSnapshotAsync updates xDS snapshot in the background
func (c *Client) updateXDSSnapshotAsync(apiID, correlationID string, isOrphaned bool) {
if c.snapshotManager == nil {
return
}
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := c.snapshotManager.UpdateSnapshot(ctx, correlationID); err != nil {
// Log level depends on operation context
if isOrphaned {
c.logger.Warn("Failed to update xDS snapshot for orphaned resource cleanup",
slog.String("api_id", apiID),
slog.Any("error", err),
)
} else {
c.logger.Error("Failed to update xDS snapshot",
slog.String("api_id", apiID),
slog.String("correlation_id", correlationID),
slog.Any("error", err),
)
}
} else if !isOrphaned {
c.logger.Info("Successfully updated xDS snapshot",
slog.String("api_id", apiID),
slog.String("correlation_id", correlationID),
)
}
}()
}
🤖 Prompt for AI Agents
In `@gateway/gateway-controller/pkg/controlplane/client.go` around lines 889 -
918, The non-orphaned log messages in updateXDSSnapshotAsync incorrectly say
"API deletion" when the call can come from undeployment (via
handleAPIUndeployedEvent), so change the logging to reflect the actual
operation: add a small parameter (e.g., op string or isUndeploy bool) to
updateXDSSnapshotAsync or compute an operation label before logging and use a
generic message like "after API change" or the op label; update the error/info
branches to use that label (referencing updateXDSSnapshotAsync and the caller
handleAPIUndeployedEvent) so logs are accurate for both deletion and
undeployment contexts.

Copy link
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

🤖 Fix all issues with AI agents
In `@gateway/gateway-controller/pkg/controlplane/api_deleted_test.go`:
- Around line 243-247: The test is not exercising policy cleanup because the
test Client is missing policyManager and apiKeyXDSManager and
StoredConfig.Configuration is incomplete; update the test's Client
initialization to include a mock/fake policyManager and apiKeyXDSManager, ensure
the test calls handleAPIDeletedEvent (or the same deletion path) so
removePolicyConfiguration is invoked on the policyManager, and add assertions
that the policyManager's RemovePolicy/RemoveAPIKey (or equivalent methods) were
called; also update createTestAPIConfigForDeletion to populate
StoredConfig.Configuration with a valid API configuration so the deletion flow
can identify keys/policies to remove.
🧹 Nitpick comments (3)
gateway/gateway-controller/pkg/controlplane/api_deleted_test.go (3)

341-385: Test does not verify policy engine cleanup — the key differentiator of delete vs. undeploy.

Per the project's design, deletion is a "hard cleanup" that should remove policies from the policy engine, unlike undeployment which preserves them. This test verifies DB and memory cleanup but doesn't assert that policies/xDS resources were cleaned up. If the production handleAPIDeletedEvent calls a policy engine or xDS manager, the test should mock and verify those interactions.

Based on learnings: "Only DELETE operations should remove policies from the policy engine. Undeployment is a soft removal that maintains state, while deletion is a hard cleanup."


454-465: Missing assertion: verify memory store cleanup proceeds despite DB errors.

The test confirms that DB operations were attempted, but doesn't check whether the in-memory store was still cleaned up after DB failures. Since deletion should be best-effort across all layers, verifying that store.Get(apiID) returns an error (config removed) would strengthen this test.

💡 Suggested additional assertion
 	if db.removeKeyCallCount != 1 {
 		t.Errorf("Expected RemoveAPIKeysAPI to be attempted, got %d", db.removeKeyCallCount)
 	}
+
+	// Verify memory cleanup still occurs despite DB errors
+	_, err := store.Get(apiID)
+	if err == nil {
+		t.Error("Expected API config to be removed from memory store despite DB errors")
+	}
 }

33-227: Consider using mock generation or interface embedding to reduce boilerplate.

The mock spans ~200 lines, most of which are no-op stubs required to satisfy the storage.Storage interface. Embedding a base no-op implementation or using a tool like mockgen/moq would significantly reduce this boilerplate and make the test file easier to maintain as the interface evolves.

Comment on lines +243 to +247
client := &Client{
logger: logger,
store: store,
db: db,
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the Client struct definition in the production code
echo "=== Client struct definition ==="
rg "type Client struct" gateway/gateway-controller/pkg/controlplane/ -A 20

echo ""
echo "=== handleAPIDeletedEvent method signature and start ==="
rg "func \(c \*Client\) handleAPIDeletedEvent" gateway/gateway-controller/pkg/controlplane/ -A 30

echo ""
echo "=== findAPIConfig method ==="
rg "func \(c \*Client\) findAPIConfig" gateway/gateway-controller/pkg/controlplane/ -A 20

Repository: wso2/api-platform

Length of output: 6379


🏁 Script executed:

#!/bin/bash
# Get the full handleAPIDeletedEvent method
echo "=== Full handleAPIDeletedEvent method ==="
sed -n '688,795p' gateway/gateway-controller/pkg/controlplane/client.go

echo ""
echo "=== Check if removePolicyConfiguration is called in handleAPIDeletedEvent ==="
rg "removePolicyConfiguration" gateway/gateway-controller/pkg/controlplane/client.go

Repository: wso2/api-platform

Length of output: 3666


🏁 Script executed:

#!/bin/bash
# Search specifically for handleAPIDeletedEvent method
echo "=== handleAPIDeletedEvent method ==="
rg "func \(c \*Client\) handleAPIDeletedEvent" -A 100 gateway/gateway-controller/pkg/controlplane/client.go | head -120

Repository: wso2/api-platform

Length of output: 3196


🏁 Script executed:

#!/bin/bash
# Get the cleanupOrphanedResources and performFullAPIDeletion methods
echo "=== cleanupOrphanedResources method ==="
rg "func \(c \*Client\) cleanupOrphanedResources" -A 50 gateway/gateway-controller/pkg/controlplane/client.go

echo ""
echo "=== performFullAPIDeletion method ==="
rg "func \(c \*Client\) performFullAPIDeletion" -A 60 gateway/gateway-controller/pkg/controlplane/client.go

Repository: wso2/api-platform

Length of output: 4016


🏁 Script executed:

#!/bin/bash
# Get more of performFullAPIDeletion to see removePolicyConfiguration call
echo "=== Full performFullAPIDeletion method ==="
rg "func \(c \*Client\) performFullAPIDeletion" -A 100 gateway/gateway-controller/pkg/controlplane/client.go | head -120

echo ""
echo "=== Check StoredConfig struct to see Configuration field ==="
rg "type StoredConfig struct" -A 20 gateway/gateway-controller/pkg/models/

Repository: wso2/api-platform

Length of output: 5657


🏁 Script executed:

#!/bin/bash
# Check the test file to see what createTestAPIConfigForDeletion creates and how it's used
echo "=== createTestAPIConfigForDeletion helper in test ==="
rg "func createTestAPIConfigForDeletion" -A 15 gateway/gateway-controller/pkg/controlplane/api_deleted_test.go

echo ""
echo "=== Check how tests use apiConfig ==="
sed -n '341,385p' gateway/gateway-controller/pkg/controlplane/api_deleted_test.go

Repository: wso2/api-platform

Length of output: 1960


Tests don't verify policy cleanup during API deletion.

The test initializes Client with only logger, store, and db, but the handleAPIDeletedEvent flow requires policyManager to complete the deletion. The production code calls removePolicyConfiguration as the final cleanup step, but since policyManager is nil in tests, this step is skipped. Per the deletion design, policies should be removed from the policy engine during DELETE operations (unlike undeployment, which preserves them). Add policyManager to the test Client initialization and add assertions verifying policy cleanup is attempted.

Additionally, the test helper createTestAPIConfigForDeletion creates a StoredConfig with only ID and Status set. For complete coverage, populate the Configuration field with a valid API configuration, and initialize apiKeyXDSManager to test that API key removal from the policy engine is also verified.

🤖 Prompt for AI Agents
In `@gateway/gateway-controller/pkg/controlplane/api_deleted_test.go` around lines
243 - 247, The test is not exercising policy cleanup because the test Client is
missing policyManager and apiKeyXDSManager and StoredConfig.Configuration is
incomplete; update the test's Client initialization to include a mock/fake
policyManager and apiKeyXDSManager, ensure the test calls handleAPIDeletedEvent
(or the same deletion path) so removePolicyConfiguration is invoked on the
policyManager, and add assertions that the policyManager's
RemovePolicy/RemoveAPIKey (or equivalent methods) were called; also update
createTestAPIConfigForDeletion to populate StoredConfig.Configuration with a
valid API configuration so the deletion flow can identify keys/policies to
remove.

Copy link
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

🤖 Fix all issues with AI agents
In `@gateway/gateway-controller/pkg/controlplane/client.go`:
- Around line 966-977: RemoveAPIKeysByAPI currently returns nil when no keys
were removed so the caller always treats the operation as having removed
something; change RemoveAPIKeysByAPI to return (int, error) (or an equivalent
removedCount and error) and update this call site to set hasOrphanedResources =
true only when removedCount > 0; adjust all other callers of RemoveAPIKeysByAPI
to handle the new (removedCount, err) signature and preserve existing error
logging via c.store.RemoveAPIKeysByAPI (now capturing count) before deciding to
flip hasOrphanedResources.
🧹 Nitpick comments (1)
gateway/gateway-controller/pkg/controlplane/client.go (1)

1013-1026: Misleading "for atomicity" comment — the operation is best-effort, not atomic.

The comment at line 1013 says "for atomicity," but the code continues cleanup even when DB deletion fails (line 1020). Consider updating the comment to reflect the actual best-effort/cascading cleanup strategy.

Comment on lines +966 to +977
// Check and clean up orphaned API keys from memory store
if err := c.store.RemoveAPIKeysByAPI(apiID); err != nil {
c.logger.Warn("Failed to remove orphaned API keys from memory store",
slog.String("api_id", apiID),
slog.Any("error", err),
)
} else {
hasOrphanedResources = true
c.logger.Debug("Checked and cleaned up orphaned API keys from memory store",
slog.String("api_id", apiID),
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

hasOrphanedResources is always set to true even when no keys existed.

RemoveAPIKeysByAPI returns nil when there are no keys to remove (see memory.go line 694-696), so the else branch on line 972 always executes and sets hasOrphanedResources = true. This means the "No orphaned resources found" log at line 1000 is effectively dead code.

Consider checking the return value or count to determine if resources were actually cleaned up, or simplify the flag logic.

🤖 Prompt for AI Agents
In `@gateway/gateway-controller/pkg/controlplane/client.go` around lines 966 -
977, RemoveAPIKeysByAPI currently returns nil when no keys were removed so the
caller always treats the operation as having removed something; change
RemoveAPIKeysByAPI to return (int, error) (or an equivalent removedCount and
error) and update this call site to set hasOrphanedResources = true only when
removedCount > 0; adjust all other callers of RemoveAPIKeysByAPI to handle the
new (removedCount, err) signature and preserve existing error logging via
c.store.RemoveAPIKeysByAPI (now capturing count) before deciding to flip
hasOrphanedResources.

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.

1 participant