Skip to content

feat: view query plan as a json#2236

Merged
SkArchon merged 10 commits intomainfrom
milinda/eng-8110-view-query-plan-as-a-json
Sep 26, 2025
Merged

feat: view query plan as a json#2236
SkArchon merged 10 commits intomainfrom
milinda/eng-8110-view-query-plan-as-a-json

Conversation

@SkArchon
Copy link
Copy Markdown
Contributor

@SkArchon SkArchon commented Sep 25, 2025

What happens now
Right now we can view query plan from the plan generator in the router using a textual pretty printed format

With this Pr
This PR introduces a "format" attribute, which is "text" by default, upon being set to "json", this would cause query plans to be generated as a raw json. This is NOT similar to the apollo jsons, to make it similar would require separate work.

Summary by CodeRabbit

  • New Features

    • CLI adds --print-format to choose plan output as text or json (case-insensitive). Defaults to text, validates input and errors on invalid values. Plan generation now emits either human-readable text or JSON.
  • Tests

    • Expanded tests cover both JSON and text outputs, validate per-file outputs and parsing, and improve temporary directory cleanup and test isolation.

Checklist

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 25, 2025

Walkthrough

Adds a PlanOutputFormat type (text/json), updates Planner.PlanOperation to accept the format and produce text or JSON, introduces a CLI --print-format flag and QueryPlanConfig.OutputFormat (defaulting to text), and updates plan generator plumbing and tests to cover both formats.

Changes

Cohort / File(s) Summary
Core planner API & formatting
router/core/plan_generator.go
Adds PlanOutputFormat type and constants (PlanOutputFormatUnset, PlanOutputFormatText, PlanOutputFormatJSON). Changes Planner.PlanOperation signature to accept outputFormat PlanOutputFormat. Returns pretty text for text, JSON-marshals the raw plan for json, and errors on unset/invalid formats or marshal failures. Adds encoding/json import.
CLI flag & config wiring
router/cmd/plan_generator.go, router/pkg/plan_generator/plan_generator.go
Adds --print-format CLI flag (case-insensitive text
Tests
router/pkg/plan_generator/plan_generator_test.go
Refactors temp dir usage to t.TempDir/t.Cleanup, updates PlanGenerator usage to account for output format, and adds sub-tests validating JSON and textual outputs with adjusted assertions and error handling.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title clearly summarizes the primary enhancement of the pull request—adding support to view query plans in JSON format—and directly aligns with the documented changes to the plan generator.
✨ Finishing touches
  • 📝 Generate Docstrings

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 678723c and 304e47a.

📒 Files selected for processing (1)
  • router/cmd/plan_generator.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/cmd/plan_generator.go
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build-router
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./events)
  • GitHub Check: image_scan
  • GitHub Check: build_test
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Sep 25, 2025

Router image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-a87fb86ecfb5ab40bf5edcad7bd9ab6f6340d4e7

@SkArchon SkArchon marked this pull request as ready for review September 25, 2025 09:06
Copy link
Copy Markdown
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
router/pkg/plan_generator/plan_generator.go (1)

145-169: Keep report file_name aligned with the operation name

When Raw is enabled we now append .json to fileName and reuse that string everywhere, including QueryPlanResult.FileName. That field previously matched the original operation file name, and downstream tooling (and even users reading report.json when print-report is enabled but print-per-file is disabled) relies on it to map results back to the source .graphql file. With this change the report will list entries such as foo.graphql.json even though the actual query file on disk is still foo.graphql, which breaks that mapping and forces consumers to strip suffixes manually.

Please keep QueryPlanResult.FileName pointing to the operation file, and derive the on-disk plan filename separately for the write step. Conceptually:

- outContent, err := planner.PlanOperation(queryFilePath, cfg.Raw)
- fileName := queryFile.Name()
- if cfg.Raw {
- 	fileName += ".json"
- }
- res := QueryPlanResult{
- 	FileName: fileName,
- 	Plan:     outContent,
- }
+ outContent, err := planner.PlanOperation(queryFilePath, cfg.Raw)
+ operationFileName := queryFile.Name()
+ planFileName := operationFileName
+ if cfg.Raw {
+ 	planFileName = operationFileName + ".json"
+ }
+ res := QueryPlanResult{
+ 	FileName: operationFileName,
+ 	Plan:     outContent,
+ }- if cfg.OutputFiles {
- 	outFileName := filepath.Join(outPath, fileName)
+ if cfg.OutputFiles {
+ 	outFileName := filepath.Join(outPath, planFileName)

That keeps the report backward-compatible while still allowing the per-file plan to use a .json suffix.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 15961bd and 6b1185b.

📒 Files selected for processing (4)
  • router/cmd/plan_generator.go (1 hunks)
  • router/core/plan_generator.go (3 hunks)
  • router/pkg/plan_generator/plan_generator.go (3 hunks)
  • router/pkg/plan_generator/plan_generator_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
router/pkg/plan_generator/plan_generator_test.go (1)
router/pkg/plan_generator/plan_generator.go (4)
  • QueryPlanConfig (22-37)
  • PlanGenerator (51-215)
  • ReportFileName (20-20)
  • QueryPlanResults (39-42)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: build_test
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./events)
  • GitHub Check: Analyze (go)
🔇 Additional comments (2)
router/cmd/plan_generator.go (1)

39-39: Thanks for wiring the CLI flag through the config

The new -raw flag integrates cleanly with the existing flag set and keeps the defaults untouched. 👍

router/core/plan_generator.go (1)

95-101: Good guardrail on raw marshaling

Returning early with a wrapped error if json.Marshal fails keeps the caller’s error-handling path unchanged and prevents us from writing partially serialized plans. Nice touch.

Comment thread router/pkg/plan_generator/plan_generator_test.go Outdated
@SkArchon SkArchon marked this pull request as draft September 25, 2025 09:15
@SkArchon
Copy link
Copy Markdown
Contributor Author

converting to draft again for a few changes

Comment thread router/cmd/plan_generator.go Outdated
Comment thread router/pkg/plan_generator/plan_generator_test.go Outdated
@SkArchon SkArchon marked this pull request as ready for review September 25, 2025 09:18
Copy link
Copy Markdown
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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6b1185b and bd2d2b7.

📒 Files selected for processing (2)
  • router/pkg/plan_generator/plan_generator.go (2 hunks)
  • router/pkg/plan_generator/plan_generator_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/pkg/plan_generator/plan_generator.go
🧰 Additional context used
🧬 Code graph analysis (1)
router/pkg/plan_generator/plan_generator_test.go (2)
router/pkg/plan_generator/plan_generator.go (2)
  • QueryPlanConfig (22-37)
  • PlanGenerator (51-211)
router/cmd/plan_generator.go (1)
  • PlanGenerator (22-112)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: build_test
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./events)
  • GitHub Check: Analyze (go)

Comment thread router/pkg/plan_generator/plan_generator_test.go
Comment thread router/pkg/plan_generator/plan_generator_test.go
Comment thread router/cmd/plan_generator.go Outdated
Copy link
Copy Markdown
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

🧹 Nitpick comments (7)
router/core/plan_generator.go (1)

56-61: Solid: explicit output format enum

Clear public type with string constants. Consider adding a short doc comment for exported identifiers to improve GoDoc.

router/pkg/plan_generator/plan_generator.go (2)

80-86: Filter parsing: trim and ignore blank lines (handle CRLF, trailing newlines)

Direct strings.Split can leave "" entries and CRLF tails, causing false negatives on Windows or with trailing newlines.

-    filter = strings.Split(string(filterContent), "\n")
+    for _, line := range strings.Split(string(filterContent), "\n") {
+        l := strings.TrimSpace(line)
+        if l != "" {
+            filter = append(filter, l)
+        }
+    }

102-107: Optional: set a sane default timeout when empty

Currently an empty cfg.Timeout will fail parsing. If the CLI already guarantees a value, ignore; otherwise default here.

-    duration, parseErr := time.ParseDuration(cfg.Timeout)
+    if cfg.Timeout == "" {
+        cfg.Timeout = "30s"
+    }
+    duration, parseErr := time.ParseDuration(cfg.Timeout)
router/pkg/plan_generator/plan_generator_test.go (4)

21-24: Use filepath.Join for filesystem paths

path.Join is for slash‑separated URLs; prefer filepath.Join for OS‑agnostic filesystem paths.

 func getTestDataDir() string {
     _, filename, _, _ := runtime.Caller(0)
-    return path.Join(filepath.Dir(filename), "testdata")
+    return filepath.Join(filepath.Dir(filename), "testdata")
 }

Consider updating other occurrences of path.Join in this file similarly.


26-35: Precompile the regex

Avoid recompiling the regex on each call to sanitizeName.

You can add this at file scope (outside the function):

var sanitizeRe = regexp.MustCompile(`[^a-zA-Z0-9_]+`)

Then inside sanitizeName:

- re := regexp.MustCompile(`[^a-zA-Z0-9_]+`)
- name = re.ReplaceAllString(name, "_")
+ name = sanitizeRe.ReplaceAllString(name, "_")

391-428: Optionally assert JSON validity for raw outputs

If you want a stronger check, validate JSON for entries that look like JSON, while still allowing Warning/Error fallbacks.

-            for _, de := range entriesInOutDir {
+            for _, de := range entriesInOutDir {
                 name := de.Name()
                 content, err := os.ReadFile(path.Join(outDir, name))
                 assert.NoError(t, err)
                 assert.Equal(t, string(results[name]), string(content))
+                s := strings.TrimSpace(string(content))
+                if strings.HasPrefix(s, "{") {
+                    var v any
+                    assert.NoError(t, json.Unmarshal(content, &v))
+                }
             }

Also consider switching path.Join to filepath.Join here.


430-467: Mirror the JSON-validity check in the text-format test (optional)

Not required, but keeping similar checks increases parity and future‑proofs the tests.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd2d2b7 and ee3b23f.

📒 Files selected for processing (21)
  • router/cmd/plan_generator.go (3 hunks)
  • router/core/plan_generator.go (4 hunks)
  • router/pkg/plan_generator/plan_generator.go (3 hunks)
  • router/pkg/plan_generator/plan_generator_test.go (9 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_a_plan_for_every_file_filtered/1.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/1.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/2.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/3.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/4.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/5.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/1.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/2.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/3.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/4.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/5.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/1.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/2.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/3.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/4.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/5.graphql (1 hunks)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_results_should_return_the_warnings_and_generate_results_file/report.json (1 hunks)
✅ Files skipped from review due to trivial changes (11)
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_a_plan_for_every_file_filtered/1.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/3.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/4.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/3.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/1.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/5.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/2.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/1.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/3.graphql
  • router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_results_should_return_the_warnings_and_generate_results_file/report.json
  • router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/2.graphql
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/cmd/plan_generator.go
🧰 Additional context used
🧬 Code graph analysis (2)
router/pkg/plan_generator/plan_generator.go (1)
router/core/plan_generator.go (2)
  • PlanOutputFormat (56-56)
  • PlanOutputFormatText (59-59)
router/pkg/plan_generator/plan_generator_test.go (2)
router/pkg/plan_generator/plan_generator.go (3)
  • QueryPlanConfig (22-37)
  • PlanGenerator (51-215)
  • ReportFileName (20-20)
router/core/plan_generator.go (3)
  • PlanGenerator (43-47)
  • PlanOutputFormatJSON (60-60)
  • PlanOutputFormatText (59-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: build-router
  • GitHub Check: image_scan
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_test
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: Analyze (go)
🔇 Additional comments (7)
router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/4.graphql (1)

1-26: LGTM on the new plan fixture.

Plan structure and entity resolution look consistent with the rest of the suite. No issues spotted.

router/pkg/plan_generator/testdata/plans/testplangenerator_will_not_fail_on_warnings_and_files_should_have_warnings_and_generate_files/1.graphql (1)

1-32: Fixture matches the expected non-JSON plan.

The sequence and fetch blocks line up with the textual formatter’s current output, so this fixture looks good to me.

router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/2.graphql (1)

1-1: Fixture LGTM

The warning fixture aligns with the generator’s "Warning:" prefix behavior.

router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/5.graphql (1)

1-29: Textual plan fixture LGTM

Matches the expected non‑raw pretty‑printed output shape.

router/pkg/plan_generator/testdata/plans/testplangenerator_generates_non_raw_textual_plans_when_raw_is_disabled/4.graphql (1)

1-26: Subscription plan fixture LGTM

Covers the subscription + entity fetch path as intended.

router/pkg/plan_generator/testdata/plans/testplangenerator_generates_raw_json_plans_when_raw_is_enabled/5.graphql (1)

1-1: Raw JSON fixture LGTM

Minified JSON matches the expected structure for OutputFormatJSON.

router/pkg/plan_generator/plan_generator.go (1)

56-58: Defaulting format to text is good

Reasonable default when not specified.

Comment thread router/core/plan_generator.go
Copy link
Copy Markdown
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: 0

🧹 Nitpick comments (3)
router/core/plan_generator.go (3)

56-62: Add brief doc comments for the new enum

Small clarity win for readers and godoc.

Apply this diff:

 type PlanOutputFormat string

 const (
-	PlanOutputFormatText PlanOutputFormat = "text"
-	PlanOutputFormatJSON PlanOutputFormat = "json"
+	// PlanOutputFormatText renders a human-readable, pretty-printed plan.
+	PlanOutputFormatText PlanOutputFormat = "text"
+	// PlanOutputFormatJSON renders the raw plan as JSON.
+	PlanOutputFormatJSON PlanOutputFormat = "json"
 )

106-110: Nit: rename temporary variable for clarity

Avoid naming the variable “marshal” (reads like a function). Use a neutral name.

Apply this diff:

-		marshal, err := json.Marshal(rawPlan)
+		b, err := json.Marshal(rawPlan)
 		if err != nil {
 			return "", fmt.Errorf("failed to marshal raw plan: %w", err)
 		}
-		return string(marshal), nil
+		return string(b), nil

113-113: Error message: prefer “output format” over “type”

More precise wording and consistent with the API.

Apply this diff:

-	return "", fmt.Errorf("invalid type specified: %q", outputFormat)
+	return "", fmt.Errorf("invalid output format: %q", outputFormat)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee3b23f and 1f1c2e8.

📒 Files selected for processing (1)
  • router/core/plan_generator.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
router/core/plan_generator.go (1)
router/pkg/pubsub/datasource/planner.go (1)
  • Planner (14-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: integration_test (./events)
  • GitHub Check: image_scan
  • GitHub Check: build_push_image
  • GitHub Check: build_test
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (3)
router/core/plan_generator.go (3)

5-5: Import of encoding/json is appropriate

Required for JSON output; no issues.


102-114: Previous “wrap nil error” bug is fixed

The default case now returns a concrete error string and no longer wraps a nil error. Good catch and fix.


76-76: Signature change verified: all call sites updated
Call in router/pkg/plan_generator/plan_generator.go passes the new outputFormat argument; no missing invocations remain.

@SkArchon SkArchon force-pushed the milinda/eng-8110-view-query-plan-as-a-json branch from d8339cb to cb39241 Compare September 25, 2025 20:31
Copy link
Copy Markdown
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: 0

🧹 Nitpick comments (4)
router/core/plan_generator.go (4)

56-62: Add brief GoDoc for exported type/constants (optional).

Improves API clarity and linting.

+// PlanOutputFormat controls how a query plan is serialized for output.
 type PlanOutputFormat string
 
 const (
-	PlanOutputFormatText PlanOutputFormat = "text"
-	PlanOutputFormatJSON PlanOutputFormat = "json"
+	// PlanOutputFormatText pretty-prints the plan as human-readable text.
+	PlanOutputFormatText PlanOutputFormat = "text"
+	// PlanOutputFormatJSON emits the raw plan as JSON.
+	PlanOutputFormatJSON PlanOutputFormat = "json"
 )

102-111: Optional: pretty-print JSON for readability.

If the JSON is meant for humans too, consider MarshalIndent.

- marshal, err := json.Marshal(rawPlan)
+ marshal, err := json.MarshalIndent(rawPlan, "", "  ")

76-86: Early-validate outputFormat to fail fast (optional).

Return on invalid formats before doing heavy planning work.

 func (pl *Planner) PlanOperation(operationFilePath string, outputFormat PlanOutputFormat) (string, error) {
+	switch outputFormat {
+	case PlanOutputFormatText, PlanOutputFormatJSON:
+		// ok
+	default:
+		return "", fmt.Errorf("invalid output format: %q", outputFormat)
+	}

113-113: Clarify error message wording (nit).

“invalid output format” is more specific than “invalid type”.

-return "", fmt.Errorf("invalid type specified: %q", outputFormat)
+return "", fmt.Errorf("invalid output format: %q", outputFormat)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f1c2e8 and cb39241.

📒 Files selected for processing (2)
  • router/cmd/plan_generator.go (3 hunks)
  • router/core/plan_generator.go (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
router/cmd/plan_generator.go (2)
router/pkg/plan_generator/plan_generator.go (1)
  • QueryPlanConfig (22-37)
router/core/plan_generator.go (3)
  • PlanOutputFormatText (59-59)
  • PlanOutputFormat (56-56)
  • PlanOutputFormatJSON (60-60)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build-router
  • GitHub Check: image_scan
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./events)
  • GitHub Check: build_push_image
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_test
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
🔇 Additional comments (6)
router/cmd/plan_generator.go (3)

10-10: LGTM: case-insensitive flag parsing.

Importing strings to normalize the flag value is appropriate.


26-28: Good: explicit default to text.

Initializing OutputFormat to PlanOutputFormatText provides a clear default.


42-52: Consider renaming flag to --format and clarify default in help.

For CLI consistency and brevity, prefer --format over --print-format, and mention the default in the help string. No behavior change.

- f.Func("print-format", "output format (text|json)", func(s string) error {
+ f.Func("format", "output format (text|json) [default: text]", func(s string) error {

If you keep --print-format, consider registering both flags to avoid churn:

+ f.Func("print-format", "DEPRECATED: use --format", func(s string) error {
+   return nil // keep for backward compatibility; ignore value
+ })
router/core/plan_generator.go (3)

5-5: LGTM: JSON import added for new output mode.

Import is correct and scoped.


102-111: Confirm JSON marshaling safety (cycles/unsupported types).

Verify resolve.FetchTreeQueryPlanNode contains only JSON-encodable fields and no parent pointers that create cycles. If not, introduce a DTO for serialization.


76-76: All PlanOperation call sites updated The call in router/pkg/plan_generator/plan_generator.go now passes cfg.OutputFormat; no further updates needed.

@SkArchon SkArchon requested a review from endigma September 26, 2025 05:09
Comment thread router/pkg/plan_generator/plan_generator_test.go Outdated
Comment thread router/core/plan_generator.go
Comment thread router/pkg/plan_generator/plan_generator_test.go Outdated
Comment thread router/cmd/plan_generator.go Outdated
@endigma endigma self-requested a review September 26, 2025 09:33
Copy link
Copy Markdown
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: 0

🧹 Nitpick comments (5)
router/cmd/plan_generator.go (2)

26-28: Avoid duplicated defaults for OutputFormat

You're defaulting OutputFormat here and (per package code) also inside pkg PlanGenerator. Prefer a single source of defaulting to prevent drift; keep it in pkg and let CLI usage text advertise the default.


42-52: Harden flag parsing (trim input) and clarify help text

Trim whitespace before lowercasing; also reflect the default in the flag help for discoverability.

Apply this diff:

-	f.Func("print-format", "output format (text|json)", func(s string) error {
-		switch value := core.PlanOutputFormat(strings.ToLower(s)); value {
+	f.Func("print-format", "output format (text|json) (default: text)", func(s string) error {
+		switch value := core.PlanOutputFormat(strings.ToLower(strings.TrimSpace(s))); value {
 		 case core.PlanOutputFormatInvalid:
 		   cfg.OutputFormat = core.PlanOutputFormatText
 		   return nil
 		 case core.PlanOutputFormatText, core.PlanOutputFormatJSON:
 		   cfg.OutputFormat = value
 		   return nil
 		}
 		return fmt.Errorf("must be one of: text, json (got %q)", s)
 	})
router/pkg/plan_generator/plan_generator_test.go (2)

82-85: Use t.TempDir consistently

This test still uses os.MkdirTemp + t.Cleanup. Prefer t.TempDir for consistency with the rest and less code.

Apply this diff:

-		tempDir, err := os.MkdirTemp("", "plans-")
-		require.NoError(t, err)
-		t.Cleanup(func() {
-			_ = os.RemoveAll(tempDir)
-		})
+		tempDir := t.TempDir()

316-349: Test names: reflect new API (optional)

Consider renaming “Raw is enabled/disabled” to “OutputFormatJSON/Text” to match the new configuration surface.

router/core/plan_generator.go (1)

114-114: Clarify error wording

“invalid type specified” could be “invalid output format” for precision and consistency with CLI wording.

Apply this diff:

-	return "", fmt.Errorf("invalid type specified: %q", outputFormat)
+	return "", fmt.Errorf("invalid output format: %q", outputFormat)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3f7ee59 and 14ca578.

📒 Files selected for processing (3)
  • router/cmd/plan_generator.go (3 hunks)
  • router/core/plan_generator.go (4 hunks)
  • router/pkg/plan_generator/plan_generator_test.go (21 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
router/pkg/plan_generator/plan_generator_test.go (2)
router/core/plan_generator.go (3)
  • PlanGenerator (43-47)
  • PlanOutputFormatJSON (61-61)
  • PlanOutputFormatText (60-60)
router/pkg/plan_generator/plan_generator.go (2)
  • PlanGenerator (51-215)
  • QueryPlanConfig (22-37)
router/cmd/plan_generator.go (2)
router/pkg/plan_generator/plan_generator.go (1)
  • QueryPlanConfig (22-37)
router/core/plan_generator.go (4)
  • PlanOutputFormatText (60-60)
  • PlanOutputFormat (56-56)
  • PlanOutputFormatInvalid (59-59)
  • PlanOutputFormatJSON (61-61)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
  • GitHub Check: build-router
  • GitHub Check: image_scan (nonroot)
  • GitHub Check: build_push_image
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./events)
  • GitHub Check: image_scan
  • GitHub Check: build_push_image (nonroot)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: build_test
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (7)
router/cmd/plan_generator.go (1)

10-10: LGTM: needed import

strings import is appropriate for case-insensitive flag parsing.

router/pkg/plan_generator/plan_generator_test.go (3)

35-35: Nice: switched to t.TempDir

Using t.TempDir reduces boilerplate and auto-cleans temp dirs.


341-347: Allow "Error:" outputs in JSON-mode assertion

PlanGenerator can write "Error: ..." (non-validation failures) even when OutputFormatJSON is set. The current assertion only allows "Warning:". Accept both to avoid false negatives. Based on relevant code snippets.

Apply this diff:

-			if err := json.Unmarshal(content, &m); err != nil {
-				assert.True(t, strings.HasPrefix(string(content), "Warning:"))
+			if err := json.Unmarshal(content, &m); err != nil {
+				s := string(content)
+				assert.True(t, strings.HasPrefix(s, "Warning:") || strings.HasPrefix(s, "Error:"))

375-379: Allow "Error:" outputs in text-mode assertion

Non-raw path can also emit "Error: ..." fallbacks. Include it in accepted prefixes to prevent false failures. Based on relevant code snippets.

Apply this diff:

-			assert.True(t, strings.HasPrefix(s, "QueryPlan {") || strings.HasPrefix(s, "Warning:"))
+			assert.True(t, strings.HasPrefix(s, "QueryPlan {") ||
+				strings.HasPrefix(s, "Warning:") ||
+				strings.HasPrefix(s, "Error:"))
router/core/plan_generator.go (3)

56-63: LGTM: explicit output format type and zero-value

PlanOutputFormat with an explicit Invalid zero-value is a solid pattern.


103-114: LGTM: format switch and JSON marshalling

Switching on outputFormat with proper error propagation is correct.


77-77: No in-repo calls to PlanOperation found; manually verify external callers – automated search returned no usages beyond its definition; ensure CLI commands, tests or other consumers supply the new outputFormat argument.

Comment thread router/cmd/plan_generator.go Outdated
Copy link
Copy Markdown
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: 0

🧹 Nitpick comments (4)
router/core/plan_generator.go (4)

56-63: Enums look good; handle zero value by defaulting to text

You introduced PlanOutputFormatUnset = "". Ensure zero-value inputs don’t error by defaulting to text in PlanOperation.


103-114: Default unset to text and clarify error message

Avoid surprising errors on zero-value format and make the error precise.

Apply:

-	switch outputFormat {
+	if outputFormat == PlanOutputFormatUnset {
+		outputFormat = PlanOutputFormatText
+	}
+	switch outputFormat {
 	case PlanOutputFormatText:
 		return rawPlan.PrettyPrint(), nil
 	case PlanOutputFormatJSON:
 		marshal, err := json.Marshal(rawPlan)
 		if err != nil {
 			return "", fmt.Errorf("failed to marshal raw plan: %w", err)
 		}
 		return string(marshal), nil
 	}
 
-	return "", fmt.Errorf("invalid type specified: %q", outputFormat)
+	return "", fmt.Errorf("invalid output format: %q", outputFormat)

107-111: Optional: support pretty JSON output variant

If human readability is desired, consider an additional format (e.g., "json-pretty") that uses json.MarshalIndent.


77-77: All PlanOperation call sites updated with the new outputFormat parameter. No remaining breaking calls detected. Optionally add a convenience wrapper

func (pl *Planner) PlanOperationText(path string) (string, error) {
    return pl.PlanOperation(path, PlanOutputFormatText)
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 14ca578 and 678723c.

📒 Files selected for processing (3)
  • router/cmd/plan_generator.go (3 hunks)
  • router/core/plan_generator.go (4 hunks)
  • router/pkg/plan_generator/plan_generator.go (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/pkg/plan_generator/plan_generator.go
  • router/cmd/plan_generator.go
🧰 Additional context used
🧬 Code graph analysis (1)
router/core/plan_generator.go (1)
router/pkg/pubsub/datasource/planner.go (1)
  • Planner (14-21)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: integration_test (./. ./fuzzquery ./lifecycle ./modules)
  • GitHub Check: integration_test (./telemetry)
  • GitHub Check: Analyze (go)
🔇 Additional comments (1)
router/core/plan_generator.go (1)

5-5: LGTM on JSON import

Needed for JSON output; no issues.

@SkArchon SkArchon merged commit 5435da3 into main Sep 26, 2025
28 checks passed
@SkArchon SkArchon deleted the milinda/eng-8110-view-query-plan-as-a-json branch September 26, 2025 10:43
@Noroth Noroth mentioned this pull request Sep 30, 2025
5 tasks
@coderabbitai coderabbitai Bot mentioned this pull request Oct 29, 2025
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants