Skip to content

[duplicate-code] Duplicate Code Pattern: Branch duplication in rewriteEnvelopeTextPayload content slice handling #7301

@github-actions

Description

@github-actions

Part of duplicate code analysis: #7299

Summary

Inside rewriteEnvelopeTextPayload in internal/middleware/jqschema.go (lines 400–452), the two case branches for rewriting the first content item share almost identical code. The only structural difference is the element type (map[string]interface{} vs interface{}); the actual mutation logic — shallow-copy the slice, shallow-copy the first item map, set "text", put it back — is identical in both branches.

Duplication Details

Pattern: Shallow-copy-and-mutate first content item

  • Severity: Medium
  • Occurrences: 2 (adjacent case branches inside the same function)
  • Location: internal/middleware/jqschema.go (lines 413–451, rewriteEnvelopeTextPayload)
  • Code Sample:
// Branch 1 — case []map[string]interface{}:
case []map[string]interface{}:
    if len(content) == 0 {
        return nil, false
    }
    rewrittenContent := append([]map[string]interface{}(nil), content...)
    firstItem := make(map[string]interface{}, len(rewrittenContent[0]))
    for key, value := range rewrittenContent[0] {
        firstItem[key] = value
    }
    firstItem["text"] = filteredText
    rewrittenContent[0] = firstItem
    rewrittenMap["content"] = rewrittenContent
    return rewrittenMap, true

// Branch 2 — case []interface{}:  (nearly identical, ~18 lines)
case []interface{}:
    if len(content) == 0 {
        return nil, false
    }
    rewrittenContent := append([]interface{}(nil), content...)
    firstItem, ok := rewrittenContent[0].(map[string]interface{})
    if !ok {
        return nil, false
    }
    rewrittenFirstItem := make(map[string]interface{}, len(firstItem))
    for key, value := range firstItem {
        rewrittenFirstItem[key] = value
    }
    rewrittenFirstItem["text"] = filteredText
    rewrittenContent[0] = rewrittenFirstItem
    rewrittenMap["content"] = rewrittenContent
    return rewrittenMap, true

Impact Analysis

  • Maintainability: Any change to the "set text in first item" logic (e.g., adding a second content item, checking the type field) must be applied twice.
  • Bug Risk: Low — the two branches are in the same function and visually adjacent, so reviewers tend to catch divergence. However, the asymmetry in handling the !ok case (the []interface{} branch already returns nil, false while the []map branch cannot fail) is a subtle difference that could mask a future bug.
  • Code Bloat: ~18 lines × 2 branches ≈ 36 lines reducible to ~18 + a small helper.

Refactoring Recommendations

  1. Extract rewriteFirstContentItem helper:

    // rewriteFirstContentItem normalises a content slice (either []interface{} or
    // []map[string]interface{}), shallow-copies the first item, sets its "text"
    // field to filteredText, and returns the updated items as []interface{}.
    // Returns nil, false if the slice is empty or the first element is not a map.
    func rewriteFirstContentItem(contentVal interface{}, filteredText string) ([]interface{}, bool)
    • Accepts the raw contentVal interface (eliminating the outer type switch)
    • Returns a []interface{} so rewrittenMap["content"] always has a consistent type
    • Estimated effort: 1 hour
    • Benefits: eliminates branch duplication, consolidates the !ok guard in one place
  2. Note: if NormalizeContentItems (see sub-issue [duplicate-code] Duplicate Code Pattern: Content Slice Normalization across mcp, mcpresult, and middleware #7300) is implemented first, this refactoring becomes simpler — use the new helper to normalize, then mutate the first item.

Implementation Checklist

Parent Issue

See parent analysis report: #7299
Related to #7299

Generated by Duplicate Code Detector · sonnet46 4.8M ·

  • expires on Jun 17, 2026, 3:44 AM UTC

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions