Skip to content

feat(api-gateway): Introduce Query format Convertion API#10286

Merged
ovr merged 13 commits intomasterfrom
feat/convert-api
Apr 15, 2026
Merged

feat(api-gateway): Introduce Query format Convertion API#10286
ovr merged 13 commits intomasterfrom
feat/convert-api

Conversation

@KSDaemon
Copy link
Copy Markdown
Contributor

@KSDaemon KSDaemon commented Jan 6, 2026

This PR introduces new API endpoint /v1/convert that allows you to convert queries between different formats. Right now only sql→rest conversion is implemented, but this might be extended in the future.

  • feat(api-gateway): Introduce /v1/convert API for query format conversion
  • prepare backend native
  • add tests

Check List

  • Tests have been run in packages where changes have been made, if available
  • Linter has been run for changed code
  • Tests for the changes have been added if not covered yet
  • Docs have been added / updated if required

@github-actions github-actions bot added rust Pull requests that update Rust code javascript Pull requests that update Javascript code labels Jan 6, 2026
Comment thread packages/cubejs-api-gateway/src/gateway.ts Fixed
@codecov
Copy link
Copy Markdown

codecov bot commented Jan 6, 2026

Codecov Report

❌ Patch coverage is 7.14286% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.81%. Comparing base (0db9e70) to head (de2ca25).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
packages/cubejs-api-gateway/src/gateway.ts 7.69% 12 Missing ⚠️
packages/cubejs-api-gateway/src/sql-server.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master   #10286      +/-   ##
==========================================
- Coverage   83.41%   78.81%   -4.61%     
==========================================
  Files         250      465     +215     
  Lines       75261    91944   +16683     
  Branches        0     3357    +3357     
==========================================
+ Hits        62780    72462    +9682     
- Misses      12481    18992    +6511     
- Partials        0      490     +490     
Flag Coverage Δ
cube-backend 58.03% <7.14%> (?)
cubesql 83.41% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@KSDaemon KSDaemon marked this pull request as ready for review January 7, 2026 13:25
@KSDaemon KSDaemon requested a review from a team as a code owner January 7, 2026 13:25
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 15, 2026

Claude finished @ovr's task in 3m 20s —— View job


Code Review: feat(api-gateway): Introduce Query format Conversion API

  • Read and analyze all changed files
  • Review code quality and best practices
  • Review security considerations
  • Review performance considerations
  • Review test coverage and quality
  • Provide comprehensive review feedback

Summary

This PR introduces a new POST /v1/convert endpoint that converts SQL queries to Cube REST API query format, leveraging the existing CubeSQL compilation pipeline via a new Rust rest4sql module. The overall design is clean and follows the existing patterns well (e.g., sql4sql). I found one definite bug, one consistency issue, and a few suggestions.

Issues Found

🔴 Bug — Wrong variable in error message

packages/cubejs-api-gateway/src/gateway.ts:1622 — The output validation error message references convertQuery.input instead of convertQuery.output. This is a copy-paste bug from the line above. See inline comment →

🟠 Bug / Consistency — Non-null assertion instead of safe accessor

packages/cubejs-api-gateway/src/sql-server.ts:88rest4sql uses this.sqlInterfaceInstance! while sql4sql and execSql both use this.getSqlInterfaceInstance() which provides a proper null-check with a descriptive error. See inline comment →

🟡 Missing input validation

packages/cubejs-api-gateway/src/gateway.ts:1613-1623 — No validation that convertQuery.query is a non-empty string before passing it to the native layer. Missing or malformed query values will produce unhelpful errors from the Rust side. See inline comment →

Security

  • Authorization: Correctly uses assertApiScope('sql', ...) consistent with the sql4sql endpoint. ✅
  • Rate limiting: GitHub Advanced Security flagged the missing rate limiter. However, no other routes in this gateway have rate limiting either, so this is a pre-existing concern, not specific to this PR. ✅
  • Input sanitization: The SQL query string is passed directly to the CubeSQL compiler which handles parsing safely. The security_context is JSON-serialized before crossing the FFI boundary. No injection risk here. ✅

Performance

  • The implementation efficiently reuses the existing CubeSQL session/compilation infrastructure. No concerns about redundant work.
  • One minor note: each rest4sql call creates a new session via with_session. This is consistent with how sql4sql works, so no issue here.

Test Coverage

The 4 test cases cover:

  • Basic measure query ✅
  • Query with dimension filter ✅
  • Query with time dimension filter ✅
  • Unconvertible query (wrapper with parameters) ✅

Missing test scenarios worth considering:

  • Error paths: invalid input/output values, missing query field, empty string query
  • Authentication: unauthenticated request
  • SQL syntax errors in the query string

The Codecov report shows 8.33% patch coverage (11 lines missing). This is because the gateway.ts convertQuery method isn't covered by unit tests — only by the smoke/integration test. The integration tests do exercise the full path though.

Design Notes

  • The ConvertQuery type in request.ts uses literal types 'sql' and 'rest', which is good for type safety and future extensibility.
  • The Rust rest4sql.rs module is well-structured with clean separation between the Neon FFI layer (rest4sql fn) and the async logic (handle_rest4sql_query).
  • Error responses from unconvertible queries return HTTP 200 with { status: "error", error: "..." } in the body. This is consistent with the pattern used elsewhere in this codebase, but worth noting that REST conventions would typically use 4xx for client errors.

Verdict

The first two issues (wrong variable in error message, non-null assertion) should be fixed before merge. The input validation suggestion is recommended but lower priority. Overall this is a solid, well-scoped addition.

Comment thread packages/cubejs-backend-native/src/rest4sql.rs Outdated
Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
Comment thread packages/cubejs-api-gateway/src/sql-server.ts Outdated
Comment thread packages/cubejs-api-gateway/src/gateway.ts Outdated
Comment on lines +107 to +125
match logical_plan {
LogicalPlan::Extension(extension) => {
if let Some(cube_scan) = extension.node.as_any().downcast_ref::<CubeScanNode>() {
return Ok(Rest4SqlResponse::Ok {
status: "ok".to_string(),
query: Box::new(cube_scan.request.clone()),
});
}

Ok(Rest4SqlResponse::Error {
status: "error".to_string(),
error: "Provided sql query can not be converted to rest query.".to_string(),
})
}
_ => Ok(Rest4SqlResponse::Error {
status: "error".to_string(),
error: "Provided sql query can not be converted to rest query.".to_string(),
}),
}
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.

Nit / Improvement: The two error arms (non-CubeScanNode extension and non-Extension plan) produce identical error responses. Consider collapsing them with a single fallthrough:

match logical_plan {
    LogicalPlan::Extension(extension) => {
        if let Some(cube_scan) = extension.node.as_any().downcast_ref::<CubeScanNode>() {
            return Ok(Rest4SqlResponse::Ok {
                status: "ok".to_string(),
                query: Box::new(cube_scan.request.clone()),
            });
        }
        Ok(Rest4SqlResponse::Error {
            status: "error".to_string(),
            error: "Provided sql query can not be converted to rest query.".to_string(),
        })
    }
    _ => Ok(Rest4SqlResponse::Error {
        status: "error".to_string(),
        error: "Provided sql query can not be converted to rest query.".to_string(),
    }),
}

Not blocking, but a future refactor could use a single error path for clarity. Also, it might be useful to include diagnostic information about why conversion failed (e.g. the plan type) to help users debug their queries.

Comment on lines +147 to +151
let native_auth_ctx = Arc::new(NativeSQLAuthContext {
user: Some(String::from("unknown")),
superuser: false,
security_context: NonDebugInRelease::from(security_context),
});
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.

Question: The hardcoded user "unknown" is used here and in sql4sql.rs. Is this intentional? When a REST API request is made with a valid JWT token, the security context is passed through, but the user field is always "unknown". Could this cause issues with any session-level authorization checks or audit logging?

ovr and others added 4 commits April 15, 2026 13:37
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Comment thread packages/cubejs-api-gateway/src/gateway.ts Dismissed
@ovr ovr merged commit 9ba2897 into master Apr 15, 2026
90 of 93 checks passed
@ovr ovr deleted the feat/convert-api branch April 15, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

javascript Pull requests that update Javascript code rust Pull requests that update Rust code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants