Skip to content

Multiple Improvements and Bug Fixes Related to Analytics Flow #2446

Merged
O-sura merged 2 commits into
wso2:mainfrom
O-sura:mcp-analytics-fix
Jul 6, 2026
Merged

Multiple Improvements and Bug Fixes Related to Analytics Flow #2446
O-sura merged 2 commits into
wso2:mainfrom
O-sura:mcp-analytics-fix

Conversation

@O-sura

@O-sura O-sura commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Purpose

This PR will contain the following bug-fixes/improvements to the existing analytics publishing flow

  • MCP Analytics Session ID should be sent as sessionId to match API Manager
  • Add missing MCP related analytics attributes such as Capability, isError
  • Fix responseContentType, requestSize missing for all API types related analytics events
  • Remove the default header forwarding(when no analytics-header-filter policy is configured)
  • Adding unit tests for the improvements

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

This PR improves analytics handling across the gateway by:

  • Aligning MCP session data with the analytics API by publishing the session identifier as sessionId.
  • Adding missing MCP analytics attributes, including capability mapping and isError handling.
  • Filling in missing analytics fields such as responseContentType and requestSize across API types.
  • Capturing response content-type into analytics metadata and using it when building events.
  • Removing the default request/response header fallback behavior when no analytics-header-filter policy is configured.
  • Updating policy manifest versions for several rate-limit modules.

Unit tests were added and updated to cover MCP analytics mapping, response content type handling, size propagation, and the revised header behavior.

Walkthrough

Changes

This PR bumps rate-limit policy versions in the gateway build manifest, and modifies gateway analytics logic across two packages. In system-policies/analytics, response content-type is captured from response headers, MCP capability is derived from the JSON-RPC method, and MCP error detection always sets a definitive boolean. In policy-engine/internal/analytics, response content-type resolution is reworked to prefer metadata over headers, request/response body sizes are added to events, and the MCP session key changes from mcp_session_id to sessionId. The Moesif publisher no longer applies hardcoded default headers when configuration is absent/invalid and now forwards requestSize into published metadata. Corresponding unit tests were added or updated across all affected files.

Sequence Diagram(s)

sequenceDiagram
  participant Envoy
  participant AnalyticsPolicy
  participant PolicyEngine
  participant Moesif

  Envoy->>AnalyticsPolicy: OnResponseHeaders(content-type)
  AnalyticsPolicy->>AnalyticsPolicy: derive Capability from JsonRpcMethod
  AnalyticsPolicy->>AnalyticsPolicy: set definitive IsError
  AnalyticsPolicy->>PolicyEngine: pass response_content_type metadata
  PolicyEngine->>PolicyEngine: resolve responseContentType (metadata -> header -> Unknown)
  PolicyEngine->>PolicyEngine: set requestSize, responseSize, mcpAnalytics.sessionId
  PolicyEngine->>Moesif: Publish(event)
  Moesif->>Moesif: populate headers only if valid JSON present
  Moesif->>Moesif: attach requestSize to metadata
Loading

Suggested reviewers: renuka-fernando, Krishanx92

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers purpose only and omits most required template sections like Goals, Approach, tests, security, and test environment. Fill in the missing template sections, especially Goals, Approach, Automation tests, Security checks, Documentation, and Test environment.
Title check ❓ Inconclusive The title is related to the analytics changes but is too generic to identify the main update. Use a specific title that names the primary analytics fixes, such as MCP sessionId, responseContentType, and header handling changes.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain modules listed in go.work or their selected dependencies"


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif.go (1)

137-166: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider extracting header-parsing logic into a helper.

The request and response header parsing blocks are nearly identical (unmarshal JSON string, check non-empty, log warnings). Extracting a shared helper like parseHeaderProperty(event.Properties, key) map[string]interface{} would reduce duplication and ease future changes.

♻️ Proposed helper extraction
+func parseHeaderProperty(properties map[string]interface{}, key string) map[string]interface{} {
+	result := map[string]interface{}{}
+	raw, ok := properties[key]
+	if !ok || raw == nil {
+		return result
+	}
+	slog.Debug("Headers (PUBLISHER): ", key, raw)
+	jsonStr, ok := raw.(string)
+	if !ok {
+		return result
+	}
+	var hMap map[string]interface{}
+	if err := json.Unmarshal([]byte(jsonStr), &hMap); err == nil && len(hMap) > 0 {
+		result = hMap
+	} else if err != nil {
+		slog.Warn("Failed to unmarshal headers", "key", key, "error", err)
+	}
+	return result
+}
+
 func (m *Moesif) Publish(event *dto.Event) {
 	...
-	headers := map[string]interface{}{}
-	if rawReqHeaders, ok := event.Properties["requestHeaders"]; ok && rawReqHeaders != nil {
-		...
-	}
-
-	rspHeaders := map[string]interface{}{}
-	if rawRspHeaders, ok := event.Properties["responseHeaders"]; ok && rawRspHeaders != nil {
-		...
-	}
+	headers := parseHeaderProperty(event.Properties, "requestHeaders")
+	rspHeaders := parseHeaderProperty(event.Properties, "responseHeaders")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif.go`
around lines 137 - 166, The request and response header parsing in moesif
publisher is duplicated and should be consolidated. Extract the repeated
JSON-unmarshal/empty-check/warning flow from the requestHeaders and
responseHeaders blocks in the publisher logic into a shared helper (for example,
a parseHeaderProperty-style function) and reuse it for both keys so future
changes only need to be made once.
gateway/system-policies/analytics/analytics.go (1)

769-788: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Nil-check on Line 785 is now dead code.

isError is unconditionally set on Line 778, so props.IsError is never nil. The props.IsError != nil disjunct on Line 785 always evaluates true, making the function always return a non-nil &props regardless of ErrorCode/ServerInfo. This matches the accompanying test expectation (props always non-nil), but the conditional is now vestigial and could mislead future maintainers into thinking a nil-return path still exists.

♻️ Suggested simplification
-	if props.IsError != nil || props.ErrorCode != nil || props.ServerInfo != nil {
-		return &props
-	}
-	return nil
+	return &props
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@gateway/system-policies/analytics/analytics.go` around lines 769 - 788, The
final nil-check in the JSON-RPC response parser is dead code because
props.IsError is always set in this block, so the return condition in the
function that builds props should be simplified to remove the props.IsError !=
nil disjunct. Update the logic around the isError assignment and the final
if/return path so the condition only reflects the fields that can actually be
nil, keeping the behavior of returning &props when any meaningful response data
is present.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In
`@gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif.go`:
- Around line 137-166: The request and response header parsing in moesif
publisher is duplicated and should be consolidated. Extract the repeated
JSON-unmarshal/empty-check/warning flow from the requestHeaders and
responseHeaders blocks in the publisher logic into a shared helper (for example,
a parseHeaderProperty-style function) and reuse it for both keys so future
changes only need to be made once.

In `@gateway/system-policies/analytics/analytics.go`:
- Around line 769-788: The final nil-check in the JSON-RPC response parser is
dead code because props.IsError is always set in this block, so the return
condition in the function that builds props should be simplified to remove the
props.IsError != nil disjunct. Update the logic around the isError assignment
and the final if/return path so the condition only reflects the fields that can
actually be nil, keeping the behavior of returning &props when any meaningful
response data is present.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9e935065-019f-4972-af6f-42190177712b

📥 Commits

Reviewing files that changed from the base of the PR and between c521f35 and e2a2195.

📒 Files selected for processing (7)
  • gateway/build-manifest.yaml
  • gateway/gateway-runtime/policy-engine/internal/analytics/analytics.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/analytics_test.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif.go
  • gateway/gateway-runtime/policy-engine/internal/analytics/publishers/moesif_test.go
  • gateway/system-policies/analytics/analytics.go
  • gateway/system-policies/analytics/analytics_test.go

@O-sura O-sura merged commit 92078a9 into wso2:main Jul 6, 2026
9 of 15 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