refactor: jq middleware improvements from gojq module review#3026
refactor: jq middleware improvements from gojq module review#3026
Conversation
- Remove duplicate integrationPayloadMetadataToMap test helper; use the
shared payloadMetadataToMap across both test files
- Document why a custom walk function is used instead of gojq's built-in
walk(f) (structural array collapse + leaf type replacement)
- Document single-result iterator contract (walk(.) produces exactly one
output)
- Add UTF-8 safety comment on preview truncation (json.Marshal output is
ASCII-clean, so byte-level slicing is safe)
- Skip redundant JSON unmarshal in WrapToolHandler when data is already a
native Go type (map[string]interface{} or []interface{})
Closes #2985
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR applies “quick-win” improvements from the gojq module review (#2985) to the jq schema middleware, focusing on documentation clarity and avoiding unnecessary JSON processing on hot paths.
Changes:
- Removed a duplicate test helper in
jqschema_integration_test.goin favor of the shared helper injqschema_test.go. - Added explanatory comments around the custom jq
walkimplementation and the single-result iterator contract. - Added a type-switch optimization in
WrapToolHandlerto avoid redundantmarshal → unmarshalwhen the payload is alreadymap[string]interface{}/[]interface{}.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| internal/middleware/jqschema.go | Adds documentation clarifications and an optimization to skip redundant unmarshaling before running gojq. |
| internal/middleware/jqschema_integration_test.go | Removes duplicated helper and reuses the existing shared test helper. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
internal/middleware/jqschema.go
Outdated
| // | ||
| // Byte-level slicing is safe here because json.Marshal produces ASCII-clean output: | ||
| // non-ASCII runes are escaped as \uXXXX sequences, so every byte boundary is a | ||
| // valid UTF-8 boundary. |
There was a problem hiding this comment.
The UTF-8 safety rationale here is incorrect: encoding/json.Marshal emits UTF-8 for non-ASCII runes (it does not generally escape them as \uXXXX). Byte-slicing payloadJSON at an arbitrary boundary can split a multi-byte UTF-8 sequence, so the preview string may be invalid UTF-8. Either adjust the truncation to a valid rune boundary (or validate/repair), or update the comment to accurately describe the behavior and its implications for downstream JSON encoding/logging.
json.Marshal emits raw UTF-8 (not \uXXXX escapes) for non-ASCII runes, so byte-level slicing can split multi-byte sequences. Adjust the cut point backward to the nearest valid rune boundary using utf8.RuneStart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Implements the quick-win improvements identified in the gojq module review (#2985).
Changes
1. Remove duplicate test helper
Deleted
integrationPayloadMetadataToMapfromjqschema_integration_test.go— it was byte-for-byte identical topayloadMetadataToMapinjqschema_test.go. Both files are inpackage middleware, so the shared helper is directly usable.2. Document custom
walkvs built-inAdded a comment block explaining why
jqSchemaFilterdefines its ownwalkfunction instead of using gojq's built-inwalk(f):walk(f)appliesfpost-recursion but preserves structurewalk(f)semantics3. Document single-result iterator contract
Added comment explaining why
iter.Next()is called only once — thewalk(.)filter produces exactly one output value.4. UTF-8 safety comment on preview truncation
Documented why byte-level slicing at
PayloadPreviewSizeis safe:json.Marshalescapes non-ASCII runes as\uXXXX, so every byte boundary is a valid UTF-8 boundary.5. Skip redundant JSON unmarshal
Added a type-switch in
WrapToolHandlerthat skips thejson.Unmarshalstep whendatais alreadymap[string]interface{}or[]interface{}. This avoids a needless marshal→unmarshal round-trip on hot paths.Closes #2985