Skip to content

Feats: use the types of gjson,the error expection,the invert and the key missing process of the condition - #1605

Merged
Calcium-Ion merged 3 commits into
QuantumNous:alphafrom
nekohy:feats-the-flexable-params-override
Aug 16, 2025
Merged

Feats: use the types of gjson,the error expection,the invert and the key missing process of the condition#1605
Calcium-Ion merged 3 commits into
QuantumNous:alphafrom
nekohy:feats-the-flexable-params-override

Conversation

@nekohy

@nekohy nekohy commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features
    • More flexible JSON override conditions: support for full/prefix/suffix/contains and numeric gt/gte/lt/lte comparisons.
    • Condition negation and optional pass-through when keys are missing.
    • AND/OR condition logic with richer evaluation.
  • Bug Fixes
    • More accurate numeric comparisons (only compare when both sides are numbers).
    • Reliable handling of missing keys during condition checks.
    • Errors are now surfaced during override application instead of silently falling back, improving transparency and stability.

@coderabbitai

coderabbitai Bot commented Aug 16, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

The PR updates relay/common/override.go to introduce generic, multi-mode condition evaluation for JSON overrides, add inversion and missing-key handling, propagate errors during evaluation, change ApplyParamOverride to return errors directly, and adjust operation parsing to support the new fields and value types.

Changes

Cohort / File(s) Summary
Override condition engine
relay/common/override.go
- ConditionOperation.Value now interface{}; added Invert and PassMissingKey
- Added multi-mode comparisons (full/prefix/suffix/contains/gt/gte/lt/lte) with helpers compareGjsonValues/compareEqual/compareNumeric
- checkConditions/checkSingleCondition now return (bool, error) with error propagation
- Missing key behavior via PassMissingKey
- ApplyParamOverride returns errors from applyOperations; legacy path retained but unused
- tryParseOperations parses generic values and new fields

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant ApplyParamOverride
  participant tryParseOperations
  participant applyOperations
  participant checkConditions
  participant compare

  Caller->>ApplyParamOverride: ApplyParamOverride(params, overrides)
  ApplyParamOverride->>tryParseOperations: parse operations (values/invert/pass_missing_key)
  tryParseOperations-->>ApplyParamOverride: operations or error
  ApplyParamOverride->>applyOperations: apply with condition checks
  applyOperations->>checkConditions: evaluate AND/OR conditions
  checkConditions->>compare: multi-mode comparisons (full/prefix/.../lte)
  compare-->>checkConditions: result or error
  checkConditions-->>applyOperations: bool or error
  applyOperations-->>ApplyParamOverride: applied ops or error
  ApplyParamOverride-->>Caller: modified params or error
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Poem

A rabbit taps the JSON tree,
Checks keys that are and those that flee.
Inverts a hop, compares a byte,
Prefix, suffix, all in sight.
If paths go missing—still I’ll know—
I nibble errors, then I go.
Override done; onward I bunn! 🐰

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • 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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

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

Actionable comments posted: 2

🔭 Outside diff range comments (1)
relay/common/override.go (1)

380-384: Handle json.Marshal error and avoid discarding it.

Discarding the error can hide issues when value contains unsupported types. Propagate it.

Apply this diff:

-	default:
-		jsonBytes, _ := json.Marshal(v)
-		if err := json.Unmarshal(jsonBytes, &newMap); err != nil {
-			return "", err
-		}
+	default:
+		jsonBytes, err := json.Marshal(v)
+		if err != nil {
+			return "", err
+		}
+		if err := json.Unmarshal(jsonBytes, &newMap); err != nil {
+			return "", err
+		}
🧹 Nitpick comments (4)
relay/common/override.go (4)

39-39: Avoid returning partial/empty payloads when applyOperations fails.

When applyOperations returns an error, return nil for the byte slice for clarity.

Apply this diff:

-		result, err := applyOperations(string(jsonData), operations)
-		return []byte(result), err
+		result, err := applyOperations(string(jsonData), operations)
+		if err != nil {
+			return nil, err
+		}
+		return []byte(result), nil

96-104: Default condition mode to "full" when absent; improves robustness.

If a condition omits mode, compareGjsonValues currently errors with “unsupported comparison mode: ”. Defaulting to full matches typical expectations and avoids surprising failures.

Apply this diff:

-									if mode, ok := condMap["mode"].(string); ok {
-										condition.Mode = mode
-									}
+									if mode, ok := condMap["mode"].(string); ok && mode != "" {
+										condition.Mode = mode
+									} else {
+										condition.Mode = "full"
+									}

Optional: consider validating that condMap["path"] is a non-empty string to avoid evaluating the whole document accidentally.


123-151: Short-circuit condition evaluation and validate unknown logic operators.

Current implementation evaluates all conditions and treats any non-AND logic as OR. Short-circuiting improves performance and validating logic reduces surprises.

Apply this diff:

-func checkConditions(jsonStr string, conditions []ConditionOperation, logic string) (bool, error) {
-	if len(conditions) == 0 {
-		return true, nil // 没有条件,直接通过
-	}
-	results := make([]bool, len(conditions))
-	for i, condition := range conditions {
-		result, err := checkSingleCondition(jsonStr, condition)
-		if err != nil {
-			return false, err
-		}
-		results[i] = result
-	}
-
-	if strings.ToUpper(logic) == "AND" {
-		for _, result := range results {
-			if !result {
-				return false, nil
-			}
-		}
-		return true, nil
-	} else {
-		for _, result := range results {
-			if result {
-				return true, nil
-			}
-		}
-		return false, nil
-	}
-}
+func checkConditions(jsonStr string, conditions []ConditionOperation, logic string) (bool, error) {
+	if len(conditions) == 0 {
+		return true, nil // 没有条件,直接通过
+	}
+	logicUpper := strings.ToUpper(strings.TrimSpace(logic))
+	if logicUpper == "" {
+		logicUpper = "OR"
+	}
+	switch logicUpper {
+	case "AND":
+		for _, condition := range conditions {
+			ok, err := checkSingleCondition(jsonStr, condition)
+			if err != nil {
+				return false, err
+			}
+			if !ok {
+				return false, nil
+			}
+		}
+		return true, nil
+	case "OR":
+		for _, condition := range conditions {
+			ok, err := checkSingleCondition(jsonStr, condition)
+			if err != nil {
+				return false, err
+			}
+			if ok {
+				return true, nil
+			}
+		}
+		return false, nil
+	default:
+		return false, fmt.Errorf("unsupported logic operator: %s", logic)
+	}
+}

180-202: Clarify non-string handling for prefix/suffix/contains.

These branches coerce values to strings; “contains” on arrays/objects will check substring of their JSON serialization, which can be surprising. Either:

  • constrain these modes to string types and return an error otherwise, or
  • explicitly document that non-strings are stringified.

If you prefer strict typing, I can propose a guarded implementation that errors on non-string types.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f8ca8d7 and 5696a62.

📒 Files selected for processing (1)
  • relay/common/override.go (5 hunks)
🔇 Additional comments (3)
relay/common/override.go (3)

12-16: Good expansion of condition expressiveness (types, invert, missing-key).

Struct changes look sensible and align with the PR goals. The generic Value plus Invert and PassMissingKey flags enable richer condition handling.


228-249: Numeric comparisons look correct.

Type checks and operators are handled cleanly with clear errors on invalid types.


270-274: Error propagation on condition evaluation is correct.

Returning the error directly here is the right choice given the PR’s “error expectation” goals.

Comment thread relay/common/override.go
Comment on lines +153 to +178
func checkSingleCondition(jsonStr string, condition ConditionOperation) (bool, error) {
value := gjson.Get(jsonStr, condition.Path)
if !value.Exists() {
return false
if condition.PassMissingKey {
return true, nil
}
return false, nil
}

valueStr := value.String()
targetStr := condition.Value
// 利用gjson的类型解析
targetBytes, err := json.Marshal(condition.Value)
if err != nil {
return false, fmt.Errorf("failed to marshal condition value: %v", err)
}
targetValue := gjson.ParseBytes(targetBytes)

result, err := compareGjsonValues(value, targetValue, strings.ToLower(condition.Mode))
if err != nil {
return false, fmt.Errorf("comparison failed for path %s: %v", condition.Path, err)
}

if condition.Invert {
result = !result
}
return result, nil
}

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.

⚠️ Potential issue

Invert is not applied when the key is missing.

If the key is missing and PassMissingKey is true, the function returns early and skips Invert, which makes invert semantics inconsistent across branches.

Apply this diff to unify result computation and always apply invert:

 func checkSingleCondition(jsonStr string, condition ConditionOperation) (bool, error) {
-	value := gjson.Get(jsonStr, condition.Path)
-	if !value.Exists() {
-		if condition.PassMissingKey {
-			return true, nil
-		}
-		return false, nil
-	}
-
-	// 利用gjson的类型解析
-	targetBytes, err := json.Marshal(condition.Value)
-	if err != nil {
-		return false, fmt.Errorf("failed to marshal condition value: %v", err)
-	}
-	targetValue := gjson.ParseBytes(targetBytes)
-
-	result, err := compareGjsonValues(value, targetValue, strings.ToLower(condition.Mode))
-	if err != nil {
-		return false, fmt.Errorf("comparison failed for path %s: %v", condition.Path, err)
-	}
-
-	if condition.Invert {
-		result = !result
-	}
-	return result, nil
+	value := gjson.Get(jsonStr, condition.Path)
+	mode := strings.ToLower(condition.Mode)
+	if mode == "" {
+		mode = "full"
+	}
+
+	var result bool
+	if !value.Exists() {
+		result = condition.PassMissingKey
+	} else {
+		// 利用gjson的类型解析
+		targetBytes, err := json.Marshal(condition.Value)
+		if err != nil {
+			return false, fmt.Errorf("failed to marshal condition value: %v", err)
+		}
+		targetValue := gjson.ParseBytes(targetBytes)
+
+		r, err := compareGjsonValues(value, targetValue, mode)
+		if err != nil {
+			return false, fmt.Errorf("comparison failed for path %s: %v", condition.Path, err)
+		}
+		result = r
+	}
+
+	if condition.Invert {
+		result = !result
+	}
+	return result, nil
 }
🤖 Prompt for AI Agents
In relay/common/override.go around lines 153 to 178, the function returns early
when the key is missing which skips applying condition.Invert; change the flow
so you compute a boolean result variable in all branches (when key missing: set
result = true if condition.PassMissingKey else result = false; when key present:
compute result via compareGjsonValues and handle errors as before), then after
all branches apply if condition.Invert { result = !result } and finally return
result, nil (or return false,error if compare returned an error). Ensure no
early returns bypass the invert logic.

Comment thread relay/common/override.go
Comment on lines +204 to +226
func compareEqual(jsonValue, targetValue gjson.Result) (bool, error) {
// 对布尔值特殊处理
if (jsonValue.Type == gjson.True || jsonValue.Type == gjson.False) &&
(targetValue.Type == gjson.True || targetValue.Type == gjson.False) {
return jsonValue.Bool() == targetValue.Bool(), nil
}

// 如果类型不同,报错
if jsonValue.Type != targetValue.Type {
return false, fmt.Errorf("compare for different types, got %v and %v", jsonValue.Type, targetValue.Type)
}

switch jsonValue.Type {
case gjson.True, gjson.False:
return jsonValue.Bool() == targetValue.Bool(), nil
case gjson.Number:
return jsonValue.Num == targetValue.Num, nil
case gjson.String:
return jsonValue.String() == targetValue.String(), nil
default:
return jsonValue.String() == targetValue.String(), nil
}
}

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.

⚠️ Potential issue

Array/object equality via string comparison is order-dependent; use deep structural equality.

Using String() for JSON types makes equality sensitive to key order and whitespace. This can cause false negatives for semantically equal objects.

Apply this diff:

 func compareEqual(jsonValue, targetValue gjson.Result) (bool, error) {
@@
 	// 如果类型不同,报错
 	if jsonValue.Type != targetValue.Type {
 		return false, fmt.Errorf("compare for different types, got %v and %v", jsonValue.Type, targetValue.Type)
 	}
 
 	switch jsonValue.Type {
 	case gjson.True, gjson.False:
 		return jsonValue.Bool() == targetValue.Bool(), nil
 	case gjson.Number:
 		return jsonValue.Num == targetValue.Num, nil
 	case gjson.String:
 		return jsonValue.String() == targetValue.String(), nil
+	case gjson.JSON:
+		// 对数组/对象进行结构化比较(与键顺序无关)
+		var left, right interface{}
+		if err := json.Unmarshal([]byte(jsonValue.Raw), &left); err != nil {
+			return false, err
+		}
+		if err := json.Unmarshal([]byte(targetValue.Raw), &right); err != nil {
+			return false, err
+		}
+		return reflect.DeepEqual(left, right), nil
 	default:
 		return jsonValue.String() == targetValue.String(), nil
 	}
 }

And add the missing import:

import "reflect"
🤖 Prompt for AI Agents
In relay/common/override.go around lines 204 to 226, the current equality
function falls back to jsonValue.String() which makes object/array comparisons
order-dependent; replace string fallback with structural comparison using
reflect.DeepEqual on jsonValue.Value() and targetValue.Value() for compound
types (arrays and objects), keep the existing precise checks for booleans,
numbers and strings, and add the missing import "reflect". Ensure you only use
reflect.DeepEqual for non-primitive JSON types (or as the default for types
other than True/False/Number/String) so semantically equal objects/arrays
compare equal regardless of key order.

@Calcium-Ion
Calcium-Ion merged commit 206ed55 into QuantumNous:alpha Aug 16, 2025
3 checks passed
@nekohy
nekohy deleted the feats-the-flexable-params-override branch August 16, 2025 19:08
x22x22 pushed a commit to x22x22/new-api that referenced this pull request Apr 24, 2026
…rams-override

Feats: use the types of gjson,the error expection,the invert and the key missing process  of the condition
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