Skip to content

1506: feat: enable scheduler rest api by default - #7

Open
martin-augment wants to merge 2 commits into
mainfrom
pr-1506-2026-03-15-11-53-36
Open

1506: feat: enable scheduler rest api by default#7
martin-augment wants to merge 2 commits into
mainfrom
pr-1506-2026-03-15-11-53-36

Conversation

@martin-augment

Copy link
Copy Markdown
Owner

1506: To review by AI

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request primarily focuses on enhancing the scheduler's REST API. It enables the REST API by default, streamlines error handling across various API endpoints using a new custom error response structure, and provides a configuration option to disable the API if needed. These changes aim to improve the usability and robustness of the scheduler's external interface.

Highlights

  • REST API Enabled by Default: The rest-api feature is now included in the default features for the scheduler, making the REST API active without explicit configuration.
  • Enhanced REST API Error Handling: A new SchedulerErrorResponse struct was introduced to provide more detailed and consistent error responses for REST API endpoints, replacing generic StatusCode returns.
  • Configurable REST API Disablement: A disable_rest configuration option was added, allowing users to explicitly disable the REST API at runtime if desired.
  • New /api/version Endpoint: A new endpoint was added to expose the scheduler's version information.
Changelog
  • ballista/scheduler/Cargo.toml
    • Added rest-api to the default features.
  • ballista/scheduler/src/api/handlers.rs
    • Modified API handler return types to SchedulerErrorResponse and updated error mapping logic.
    • Imported SchedulerErrorResponse for use in API handlers.
  • ballista/scheduler/src/api/mod.rs
    • Registered a new /api/version route.
  • ballista/scheduler/src/config.rs
    • Introduced disable_rest boolean field in Config and SchedulerConfig for runtime control of the REST API.
    • Updated Default and TryFrom implementations to support the new disable_rest field.
  • ballista/scheduler/src/scheduler_process.rs
    • Implemented SchedulerErrorResponse struct and its IntoResponse trait for standardized error responses.
    • Adjusted the Axum router setup to conditionally enable/disable the REST API based on the disable_rest configuration.
    • Imported Json and IntoResponse for Axum integration.
Activity
  • martin-augment created this pull request.
  • The pull request is marked for review by AI.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai

coderabbitai Bot commented Mar 15, 2026

Copy link
Copy Markdown

Walkthrough

The changes introduce structured error responses for the REST API layer, replacing StatusCode error returns with a new SchedulerErrorResponse type across multiple handler methods. Compile-time and runtime toggles are added for REST API control through feature flags and configuration settings. A new "/api/version" endpoint is added to the routing, and the default feature set is extended to include rest-api. Error handling is unified with JSON payload responses for consistency.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch pr-1506-2026-03-15-11-53-36
📝 Coding Plan
  • Generate coding plan for human review comments

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 and usage tips.

@claude

claude Bot commented Mar 15, 2026

Copy link
Copy Markdown

Code Review

Overall this PR makes a reasonable improvement by enabling the REST API by default and introducing a structured JSON error response type. Here are my findings:


Bugs / Correctness Issues

1. /api/version reuses the wrong handler

// api/mod.rs
.route("/api/version", get(handlers::get_scheduler_state::<T, U>))

/api/version points to get_scheduler_state, which returns the scheduler's full state (start time + version). A version endpoint should return only the version string. As-is, both /api/state and /api/version return identical payloads, which is confusing.

2. get_job_dot_graph / get_query_stage_dot_graph return Ok("Not Found") instead of 404

} else {
    Ok("Not Found".to_string())  // handlers.rs:426 and ~448
}

When the job doesn't exist these handlers silently return HTTP 200 with body "Not Found". All other handlers use SchedulerErrorResponse::new(StatusCode::NOT_FOUND) in this situation. These two should be updated for consistency.


Design / Architecture

3. SchedulerErrorResponse lives in the wrong module
The struct is defined in scheduler_process.rs but is exclusively used by HTTP API handlers. Placing it in api/mod.rs or a new api/errors.rs would better reflect its purpose and avoid the awkward reverse dependency where api/handlers.rs imports from scheduler_process.

4. disable_rest is a negative boolean (double-negative logic)
A field named disable_rest: false reads as "don't disable it", which is harder to reason about than enable_rest: true. Since the feature is now on by default, disable_rest makes sense as an opt-out, but worth noting in docs. At minimum the help text typo should be fixed:

// config.rs
help = "Should the rest api be disable"   // ← missing 'd'

Should be: "Disable the REST API"


Security / Information Disclosure

5. Internal error messages exposed in HTTP responses

SchedulerErrorResponse::with_error(
    StatusCode::INTERNAL_SERVER_ERROR,
    format!("Error occurred while getting the execution graph for job '{job_id}' reason: {}", err),
)

The err value (which may contain internal paths, DB connection strings, stack details, etc.) is forwarded directly to the HTTP client. Consider logging the full error server-side and returning only a safe, generic message in the response body.


Minor / Style

6. Error context is dropped silently in several places

// Several handlers still use:
.map_err(|_| SchedulerErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR))?

The |_| discards the underlying error without logging it. At minimum the error should be logged before being swallowed, as was done in the get_job handler.

7. SchedulerErrorResponse is not Clone or Display
Not blocking, but since this is a public error type it might be useful to derive these for downstream use.


Test Coverage

No tests were added for:

  • SchedulerErrorResponse serialization (verify JSON shape)
  • The disable_rest flag (confirm /api/* returns 404 when disabled)
  • The new /api/version route

At minimum a unit test for SchedulerErrorResponse::into_response() would be valuable to prevent future regressions on the JSON contract.


Summary

Category Finding
Bug /api/version returns full state, not just version
Bug get_job_dot_graph / get_query_stage_dot_graph return 200 "Not Found"
Design SchedulerErrorResponse belongs in the api module
Security Internal error details leaked in 500 responses
Minor Typo in disable_rest help text
Minor Several `
Tests No coverage for new struct or disable_rest behavior

The core direction (structured JSON errors, default-on REST API, runtime disable flag) is solid. Addressing the two bugs and the security concern would be the priority items before merging.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request enables the scheduler's REST API by default, which is a great enhancement for usability. The implementation includes a new configuration flag --disable-rest to opt-out, and improves API error handling by introducing a structured SchedulerErrorResponse.

My review includes a couple of minor suggestions to improve the new API endpoint for versioning and to fix a typo in a command-line help message. Overall, the changes are well-implemented and align with the goal of the pull request.

) -> Router {
let router = Router::new()
.route("/api/state", get(handlers::get_scheduler_state::<T, U>))
.route("/api/version", get(handlers::get_scheduler_state::<T, U>))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The /api/version endpoint reuses the get_scheduler_state handler, which returns both the scheduler start time and the version. For an endpoint named /api/version, it would be more idiomatic and less surprising for clients if it returned only the version information.

Consider creating a new, simpler handler for this route that doesn't require state and returns only the version.

For example, you could add a new handler in ballista/scheduler/src/api/handlers.rs:

#[derive(Debug, serde::Serialize)]
pub struct VersionResponse {
    version: &'static str,
}

pub async fn get_version() -> impl IntoResponse {
    Json(VersionResponse {
        version: BALLISTA_VERSION,
    })
}

And then update this line to use it:

.route("/api/version", get(handlers::get_version))

This would make the API clearer and more maintainable.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

value:good-but-wont-fix; category:bug; feedback: The Gemini AI reviewer is correct! The new REST endpoint /api/version is exactly the same as /api/state. But the goal is to extend the /api/state endpoint with more information in a follow-up Pull Request. Any user who don't need the extra information should use the new /api/version endpoint.


#[cfg(feature = "rest-api")]
/// Should the rest api be disabled
#[arg(long, default_value_t = false, help = "Should the rest api be disable")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

There's a small typo in the help message. It should be 'disabled' instead of 'disable'.

Suggested change
#[arg(long, default_value_t = false, help = "Should the rest api be disable")]
#[arg(long, default_value_t = false, help = "Should the rest api be disabled")]

@augmentcode

augmentcode Bot commented Mar 15, 2026

Copy link
Copy Markdown
🤖 Augment PR Summary

Summary: This PR enables the scheduler REST API by default and standardizes REST-facing error handling.

Changes:

  • Enable the scheduler crate’s rest-api feature in default features.
  • Add a runtime --disable-rest flag (propagated into SchedulerConfig) to turn REST routing off at startup.
  • Introduce SchedulerErrorResponse (JSON + HTTP status) implementing IntoResponse.
  • Update REST handlers to return SchedulerErrorResponse instead of bare StatusCode.
  • Adjust Axum router setup so unmatched routes return JSON 404s and so /api/* is explicitly rejected when REST is disabled (compile-time or runtime).
  • Add an /api/version route (currently wired to the same handler as /api/state).

Technical Notes: gRPC and REST continue to share the same Axum server/router; error responses are now consistently JSON-encoded with status codes.

🤖 Was this summary useful? React with 👍 or 👎

@augmentcode augmentcode Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

) -> Router {
let router = Router::new()
.route("/api/state", get(handlers::get_scheduler_state::<T, U>))
.route("/api/version", get(handlers::get_scheduler_state::<T, U>))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

/api/version is currently routed to get_scheduler_state, so it returns the full state payload (including started) and duplicates /api/state; is that intentional for clients expecting a version-only endpoint? If not, consider wiring this path to a dedicated version handler.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

value:good-but-wont-fix; category:bug; feedback: The Augment AI reviewer is correct! The new REST endpoint /api/version is exactly the same as /api/state. But the goal is to extend the /api/state endpoint with more information in a follow-up Pull Request. Any user who don't need the extra information should use the new /api/version endpoint.

tracing::error!("Error occurred while getting the execution graph for job '{job_id}': {err:?}");
StatusCode::INTERNAL_SERVER_ERROR
tracing::error!("Error occurred while getting the execution graph for job '{job_id}' reason: {err:?}");
SchedulerErrorResponse::with_error(StatusCode::INTERNAL_SERVER_ERROR, format!("Error occurred while getting the execution graph for job '{job_id}' reason: {}", err))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Several 500 responses now embed the underlying error string in the HTTP JSON via SchedulerErrorResponse::with_error(...). Can you confirm this level of detail is OK to expose to REST clients (it can leak internal information) vs logging it and returning a generic message?

Severity: medium

Other Locations
  • ballista/scheduler/src/api/handlers.rs:218
  • ballista/scheduler/src/api/handlers.rs:288

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

value:useful; category:bug; feedback: The Augment AI reviewer is correct! The internal errors should not be added to the JSON responses as is, because they may contain sensitive information. The errors should be just logged and the JSON response should contain some generic information about the problem. Only users with access to the logs should see the full details.


#[cfg(feature = "rest-api")]
/// Should the rest api be disabled
#[arg(long, default_value_t = false, help = "Should the rest api be disable")]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The --disable-rest help text is user-facing and currently says "Should the rest api be disable" (grammar/typo).

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

) -> Router {
let router = Router::new()
.route("/api/state", get(handlers::get_scheduler_state::<T, U>))
.route("/api/version", get(handlers::get_scheduler_state::<T, U>))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Version endpoint incorrectly uses scheduler state handler

Medium Severity

The new /api/version route is wired to handlers::get_scheduler_state, the same handler used by /api/state. This handler returns a SchedulerStateResponse containing both started (the scheduler start timestamp) and version, making it a duplicate of /api/state rather than a dedicated version endpoint. Consumers hitting /api/version would receive unexpected extra fields (like started), and the two routes are fully redundant.

Fix in Cursor Fix in Web

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

value:good-but-wont-fix; category:bug; feedback: The Bugbot AI reviewer is correct! The new REST endpoint /api/version is exactly the same as /api/state. But the goal is to extend the /api/state endpoint with more information in a follow-up Pull Request. Any user who don't need the extra information should use the new /api/version endpoint.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
ballista/scheduler/src/api/handlers.rs (1)

281-345: ⚠️ Potential issue | 🟠 Major

Return 404 when the job is missing.

The else branch currently returns 200 {"stages":[]} for an unknown job_id. That makes a missing job indistinguishable from a job with no stages and is inconsistent with get_job and cancel_job. Return Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND)) instead.

Suggested fix
-    } else {
-        Ok(Json(QueryStagesResponse { stages: vec![] }))
-    }
+    } else {
+        Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND))
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ballista/scheduler/src/api/handlers.rs` around lines 281 - 345, The handler
currently treats a missing job (None from
data_server.state.task_manager.get_job_execution_graph) as
Ok(Json(QueryStagesResponse { stages: vec![] })), but should return a 404 like
get_job and cancel_job; replace the else branch to return
Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND)) when graph is None
(ensure you reference get_job_execution_graph, QueryStagesResponse and
SchedulerErrorResponse), and add any needed imports so the NOT_FOUND status
compiles.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ballista/scheduler/src/api/handlers.rs`:
- Around line 421-433: The DOT endpoints currently return Ok("Not Found") when
get_job_execution_graph returns None; change those branches to return a
structured 404 error instead of a 200 string. Replace the else return in the
functions that call data_server.state.task_manager.get_job_execution_graph(...)
(the handlers that then call ExecutionGraphDot::generate) so they return
Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND)) when the job graph is
missing; apply the same change to both DOT-related endpoints (the functions that
invoke ExecutionGraphDot::generate and the similar SVG endpoint).
- Around line 174-177: The map_err closures in handlers.rs that currently log
the full backend error but return err.to_string() to clients (e.g., the closure
building the "Error occurred while getting the execution graph for job
'{job_id}' reason: {err:?}" message) must be changed to avoid leaking internals:
keep tracing::error!(.., err = ?err) to log the detailed error, but replace the
returned SchedulerErrorResponse body with a fixed, client-safe message like
"Internal server error while retrieving execution graph" (do similarly for the
other map_err branches referenced around the other handlers at the indicated
ranges). Update the closures that construct SchedulerErrorResponse (the map_err
for the execution graph and the similar closures at the other ranges) so the
status stays INTERNAL_SERVER_ERROR but the formatted message is a constant,
non-sensitive string while logging includes the original err for diagnostics.

In `@ballista/scheduler/src/config.rs`:
- Around line 253-255: The new public field disable_rest on SchedulerConfig is a
breaking change; instead make the field private and feature-gated or move it out
of the public struct surface and expose feature-gated accessors or a builder;
specifically, remove or change the pub disable_rest to a private field inside
SchedulerConfig (or to a separate internal config struct), add a getter method
(e.g., SchedulerConfig::disable_rest() behind the rest-api feature) or introduce
a builder for constructing SchedulerConfig with that option, or mark
SchedulerConfig #[non_exhaustive] if you intend to accept the breaking change in
a major release—pick one approach and apply it consistently so downstream crates
aren't broken by feature-flag variations.

---

Outside diff comments:
In `@ballista/scheduler/src/api/handlers.rs`:
- Around line 281-345: The handler currently treats a missing job (None from
data_server.state.task_manager.get_job_execution_graph) as
Ok(Json(QueryStagesResponse { stages: vec![] })), but should return a 404 like
get_job and cancel_job; replace the else branch to return
Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND)) when graph is None
(ensure you reference get_job_execution_graph, QueryStagesResponse and
SchedulerErrorResponse), and add any needed imports so the NOT_FOUND status
compiles.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bd94c56f-6cb8-4e7b-838b-5f603a6e1cd2

📥 Commits

Reviewing files that changed from the base of the PR and between e19d796 and 96490ae.

📒 Files selected for processing (5)
  • ballista/scheduler/Cargo.toml
  • ballista/scheduler/src/api/handlers.rs
  • ballista/scheduler/src/api/mod.rs
  • ballista/scheduler/src/config.rs
  • ballista/scheduler/src/scheduler_process.rs

Comment on lines 174 to 177
.map_err(|err| {
tracing::error!("Error occurred while getting the execution graph for job '{job_id}': {err:?}");
StatusCode::INTERNAL_SERVER_ERROR
tracing::error!("Error occurred while getting the execution graph for job '{job_id}' reason: {err:?}");
SchedulerErrorResponse::with_error(StatusCode::INTERNAL_SERVER_ERROR, format!("Error occurred while getting the execution graph for job '{job_id}' reason: {}", err))
})?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't serialize raw backend errors into the public REST body.

These branches return err/e.to_string() directly to clients. With REST now default-on, that leaks internal details and makes the HTTP contract depend on unstable backend error text. Log the full error, but return a fixed client-safe message here.

Example hardening pattern
-            SchedulerErrorResponse::with_error(
-                StatusCode::INTERNAL_SERVER_ERROR,
-                format!("Error getting job status: {}", err),
-            )
+            SchedulerErrorResponse::with_error(
+                StatusCode::INTERNAL_SERVER_ERROR,
+                "Failed to get job status".to_string(),
+            )

Apply the same pattern to the other handlers in this block.

Also applies to: 218-221, 287-291

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ballista/scheduler/src/api/handlers.rs` around lines 174 - 177, The map_err
closures in handlers.rs that currently log the full backend error but return
err.to_string() to clients (e.g., the closure building the "Error occurred while
getting the execution graph for job '{job_id}' reason: {err:?}" message) must be
changed to avoid leaking internals: keep tracing::error!(.., err = ?err) to log
the detailed error, but replace the returned SchedulerErrorResponse body with a
fixed, client-safe message like "Internal server error while retrieving
execution graph" (do similarly for the other map_err branches referenced around
the other handlers at the indicated ranges). Update the closures that construct
SchedulerErrorResponse (the map_err for the execution graph and the similar
closures at the other ranges) so the status stays INTERNAL_SERVER_ERROR but the
formatted message is a constant, non-sensitive string while logging includes the
original err for diagnostics.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

value:useful; category:bug; feedback: The CodeRabbit AI reviewer is correct! The internal errors should not be added to the JSON responses as is, because they may contain sensitive information. The errors should be just logged and the JSON response should contain some generic information about the problem. Only users with access to the logs should see the full details.

Comment on lines +421 to 433
) -> Result<String, SchedulerErrorResponse> {
if let Some(graph) = data_server
.state
.task_manager
.get_job_execution_graph(&job_id)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
.map_err(|_| SchedulerErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR))?
{
ExecutionGraphDot::generate(graph.as_ref())
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
.map_err(|_| SchedulerErrorResponse::new(StatusCode::INTERNAL_SERVER_ERROR))
} else {
Ok("Not Found".to_string())
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

The DOT endpoints shouldn't succeed on a missing job.

Line 432 and Line 453 return plain "Not Found" with HTTP 200. That breaks REST semantics, and get_job_svg_graph downstream turns the same condition into 400 Cannot parse graph. Return a structured 404 here instead.

Suggested fix
-    } else {
-        Ok("Not Found".to_string())
-    }
+    } else {
+        Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND))
+    }

Apply the same change in both endpoints.

Also applies to: 442-454

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ballista/scheduler/src/api/handlers.rs` around lines 421 - 433, The DOT
endpoints currently return Ok("Not Found") when get_job_execution_graph returns
None; change those branches to return a structured 404 error instead of a 200
string. Replace the else return in the functions that call
data_server.state.task_manager.get_job_execution_graph(...) (the handlers that
then call ExecutionGraphDot::generate) so they return
Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND)) when the job graph is
missing; apply the same change to both DOT-related endpoints (the functions that
invoke ExecutionGraphDot::generate and the similar SVG endpoint).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

value:good-to-have; category:bug; feedback: This code is not changed in the reviewed Pull Request but the CodeRabbit AI reviewer is correct! Instead of returning status=200 with text body "Not found" these endpoints should return 404 with an optional JSON body with more details.

Comment on lines +253 to +255
#[cfg(feature = "rest-api")]
/// Should the rest api be disabled
pub disable_rest: bool,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== rest-api default feature ==\n'
rg -n -C2 '^default = .*rest-api' ballista/scheduler/Cargo.toml

printf '\n== SchedulerConfig definition ==\n'
rg -n -C2 'pub struct SchedulerConfig|#\[non_exhaustive\]|disable_rest' ballista/scheduler/src/config.rs

printf '\n== SchedulerConfig struct literals in the repo ==\n'
rg -n -C2 --type=rust '\bSchedulerConfig\s*\{'

Repository: martin-augment/datafusion-ballista

Length of output: 3848


Avoid adding a public field to SchedulerConfig without semver protection.

SchedulerConfig is public and exhaustively constructible (no #[non_exhaustive]). Adding disable_rest breaks downstream crates that construct it via struct literals, and because rest-api is a default feature (Line 37, Cargo.toml), this affects default builds. The field addition under #[cfg(feature = "rest-api")] also causes the struct's shape to vary by feature flag, compounding the breaking change.

Either keep this field out of the public struct surface, use a builder, or defer to the next breaking release.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ballista/scheduler/src/config.rs` around lines 253 - 255, The new public
field disable_rest on SchedulerConfig is a breaking change; instead make the
field private and feature-gated or move it out of the public struct surface and
expose feature-gated accessors or a builder; specifically, remove or change the
pub disable_rest to a private field inside SchedulerConfig (or to a separate
internal config struct), add a getter method (e.g.,
SchedulerConfig::disable_rest() behind the rest-api feature) or introduce a
builder for constructing SchedulerConfig with that option, or mark
SchedulerConfig #[non_exhaustive] if you intend to accept the breaking change in
a major release—pick one approach and apply it consistently so downstream crates
aren't broken by feature-flag variations.

@martin-augment

Copy link
Copy Markdown
Owner Author

1. /api/version reuses the wrong handler

// api/mod.rs
.route("/api/version", get(handlers::get_scheduler_state::<T, U>))

/api/version points to get_scheduler_state, which returns the scheduler's full state (start time + version). A version endpoint should return only the version string. As-is, both /api/state and /api/version return identical payloads, which is confusing.

value:good-but-wont-fix; category:bug; feedback: The Claude AI reviewer is correct! The new REST endpoint /api/version is exactly the same as /api/state. But the goal is to extend the /api/state endpoint with more information in a follow-up Pull Request. Any user who don't need the extra information should use the new /api/version endpoint.

@martin-augment

Copy link
Copy Markdown
Owner Author

2. get_job_dot_graph / get_query_stage_dot_graph return Ok("Not Found") instead of 404

} else {
    Ok("Not Found".to_string())  // handlers.rs:426 and ~448
}

When the job doesn't exist these handlers silently return HTTP 200 with body "Not Found". All other handlers use SchedulerErrorResponse::new(StatusCode::NOT_FOUND) in this situation. These two should be updated for consistency.

value:good-to-have; category:bug; feedback: This code is not changed in the reviewed Pull Request but the Claude AI reviewer is correct! Instead of returning status=200 with text body "Not found" these endpoints should return 404 with an optional JSON body with more details.

@martin-augment

Copy link
Copy Markdown
Owner Author

3. SchedulerErrorResponse lives in the wrong module
The struct is defined in scheduler_process.rs but is exclusively used by HTTP API handlers. Placing it in api/mod.rs or a new api/errors.rs would better reflect its purpose and avoid the awkward reverse dependency where api/handlers.rs imports from scheduler_process.

value:useful; category:bug; feedback: The Claude AI reviewer is correct! The idea of the Pull Request author is to provide a minimal REST API that catches all requests to /api/* and returns an error explaining that the REST API cargo feature is not enabled. But to implement it he uses the always enabled gRPC-related modules. Instead he should use #[cfg(not(feature="rest-api"))] in the api/mod.rs module. This way all the REST API related code would stay in the same module

@martin-augment

Copy link
Copy Markdown
Owner Author

281-345: ⚠️ Potential issue | 🟠 Major

Return 404 when the job is missing.

The else branch currently returns 200 {"stages":[]} for an unknown job_id. That makes a missing job indistinguishable from a job with no stages and is inconsistent with get_job and cancel_job. Return Err(SchedulerErrorResponse::new(StatusCode::NOT_FOUND)) instead.

value:good-to-have; category:bug; feedback: This code is not changed in the reviewed Pull Request but the CodeRAbbit AI reviewer is correct! Instead of returning status=200 with text body "Not found" these endpoints should return 404 with an optional JSON body with more details.

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