Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions docs-mintlify/reference/core-data-apis/rest-api/reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,71 @@ Response:
}
```

## `{base_path}/v1/convert-query`

Takes an API query in the specified input format and converts it to the specified
output format. Currently, only conversion from [SQL API](/reference/core-data-apis/sql-api) queries to
[REST API](/reference/core-data-apis/rest-api) queries is supported.

This endpoint is useful for translating SQL API queries into equivalent REST API
queries that can be used with the [`/v1/load`](#base_path%2Fv1%2Fload) endpoint.

Request parameters (JSON body):

| Parameter, type | Description | Required |
| --- | --- | --- |
| `input`, `string` | Input query format. Use `sql` for [SQL API](/reference/core-data-apis/sql-api) queries | ✅ Yes |
| `output`, `string` | Output query format. Use `rest` for [REST API](/reference/core-data-apis/rest-api) queries | ✅ Yes |
| `query`, `string` | Input query in the specified format | ✅ Yes |

The response will contain a JSON object with the following properties:

| Property, type | Description |
| --- | --- |
| `status`, `string` | Query conversion status, `ok` or `error` |
| `query`, `object` | Converted query in the [REST API query format](/reference/core-data-apis/rest-api/query-format) (only present when `status` is `ok`) |
| `error`, `string` | Error message (only present when `status` is `error`) |

An error will be returned if the input query can't be converted to the specified
output format, e.g., if the SQL API query requires post-processing on top of
REST API capabilities or if the SQL API query results in multiple REST API queries.

### Example

Request to convert a SQL API query with a filter:

```bash
curl \
-X POST \
-H "Authorization: TOKEN" \
-H "Content-Type: application/json" \
-d '{"input": "sql", "output": "rest", "query": "SELECT MEASURE(total_amount) FROM orders WHERE status = '\''foo'\''"}' \
http://localhost:4000/cubejs-api/v1/convert-query
```

Successful response:

```json
{
"status": "ok",
"query": {
"measures": [
"orders.total_amount"
],
"dimensions": [],
"filters": [
{
"member": "orders.status",
"operator": "equals",
"values": ["foo"]
}
],
"segments": [],
"order": []
}
}
```

## `{base_path}/v1/meta`

<Info>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,71 @@ Response:
}
```

## `{base_path}/v1/convert-query`

Takes an API query in the specified input format and converts it to the specified
output format. Currently, only conversion from [SQL API][ref-sql-api] queries to
[REST API][ref-rest-api] queries is supported.

This endpoint is useful for translating SQL API queries into equivalent REST API
queries that can be used with the [`/v1/load`](#base_pathv1load) endpoint.

Request parameters (JSON body):

| Parameter, type | Description | Required |
| --- | --- | --- |
| `input`, `string` | Input query format. Use `sql` for [SQL API][ref-sql-api] queries | ✅ Yes |
| `output`, `string` | Output query format. Use `rest` for [REST API][ref-rest-api] queries | ✅ Yes |
| `query`, `string` | Input query in the specified format | ✅ Yes |

The response will contain a JSON object with the following properties:

| Property, type | Description |
| --- | --- |
| `status`, `string` | Query conversion status, `ok` or `error` |
| `query`, `object` | Converted query in the [REST API query format][ref-rest-api-query-format] (only present when `status` is `ok`) |
| `error`, `string` | Error message (only present when `status` is `error`) |

An error will be returned if the input query can't be converted to the specified
output format, e.g., if the SQL API query requires post-processing on top of
REST API capabilities or if the SQL API query results in multiple REST API queries.

### Example

Request to convert a SQL API query with a filter:

```bash
curl \
-X POST \
-H "Authorization: TOKEN" \
-H "Content-Type: application/json" \
-d '{"input": "sql", "output": "rest", "query": "SELECT MEASURE(total_amount) FROM orders WHERE status = '\''foo'\''"}' \
http://localhost:4000/cubejs-api/v1/convert-query
```

Successful response:

```json
{
"status": "ok",
"query": {
"measures": [
"orders.total_amount"
],
"dimensions": [],
"filters": [
{
"member": "orders.status",
"operator": "equals",
"values": ["foo"]
}
],
"segments": [],
"order": []
}
}
```

## `{base_path}/v1/meta`

<InfoBox>
Expand Down Expand Up @@ -889,6 +954,7 @@ Keep-Alive: timeout=5
[ref-datasources]: /product/configuration/advanced/multiple-data-sources
[ref-sql-api]: /product/apis-integrations/sql-api
[ref-rest-api]: /product/apis-integrations/rest-api
[ref-rest-api-query-format]: /product/apis-integrations/rest-api/query-format
[ref-regular-queries]: /product/apis-integrations/queries#regular-query
[ref-query-wpp]: /product/apis-integrations/queries#query-with-post-processing
[ref-query-wpd]: /product/apis-integrations/queries#query-with-pushdown
Expand Down
38 changes: 38 additions & 0 deletions packages/cubejs-api-gateway/src/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import {
SqlApiRequest,
MetaResponseResultFn,
RequestQuery,
QueryConvertRequest,
} from './types/request';
import {
CheckAuthInternalOptions,
Expand Down Expand Up @@ -418,6 +419,14 @@ class ApiGateway {
});
}));

app.post(`${this.basePath}/v1/convert-query`, jsonParser, userMiddlewares, userAsyncHandler(async (req, res) => {
await this.convertQuery({
payload: req.body,
context: req.context,
res: this.resToResultFn(res)
});
}));

/** **************************************************************
* meta scope *
*************************************************************** */
Expand Down Expand Up @@ -1601,6 +1610,35 @@ class ApiGateway {
};
}

protected async convertQuery({ payload, context, res }: QueryConvertRequest) {
try {
await this.assertApiScope('sql', context.securityContext);

if (payload.input !== 'sql') {
throw new Error(`Unexpected input parameter value '${payload.input}'`);
}

if (payload.output !== 'rest') {
throw new Error(`Unexpected output parameter value '${payload.output}'`);
}

if (typeof payload.query !== 'string' || !payload.query.trim()) {
throw new Error('query parameter must be a non-empty string');
}

const result = await this.sqlServer.rest4sql(payload.query, context.securityContext);

await res(result);
} catch (e: any) {
this.handleError({
e,
context,
query: payload,
res,
});
}
}

protected async dryRun({ query, context, res }: QueryRequest) {
const requestStarted = new Date();

Expand Down
6 changes: 6 additions & 0 deletions packages/cubejs-api-gateway/src/sql-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import {
shutdownInterface,
execSql,
sql4sql,
rest4sql,
SqlInterfaceInstance,
Request as NativeRequest,
LoadRequestMeta,
Sql4SqlResponse,
QueryConvertResponse,
} from '@cubejs-backend/native';
import type { ShutdownMode } from '@cubejs-backend/native';
import { displayCLIWarning, getEnv, CacheMode } from '@cubejs-backend/shared';
Expand Down Expand Up @@ -82,6 +84,10 @@ export class SQLServer {
return sql4sql(this.getSqlInterfaceInstance(), sqlQuery, disablePostProcessing, securityContext);
}

public async rest4sql(sqlQuery: string, securityContext?: unknown): Promise<QueryConvertResponse> {
return rest4sql(this.getSqlInterfaceInstance(), sqlQuery, securityContext);
}

protected buildCheckSqlAuth(options: SQLServerOptions): CheckSQLAuthFn {
return (options.checkSqlAuth && this.wrapCheckSqlAuthFn(options.checkSqlAuth))
|| this.createDefaultCheckSqlAuthFn(options);
Expand Down
12 changes: 12 additions & 0 deletions packages/cubejs-api-gateway/src/types/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ type QueryRequest = BaseRequest & {
cacheMode?: CacheMode;
};

type ConvertQuery = {
input: 'sql';
output: 'rest';
query: string;
};

type QueryConvertRequest = BaseRequest & {
payload: ConvertQuery;
};

type SqlApiRequest = BaseRequest & {
query: Record<string, any>;
sqlQuery?: [string, string[]];
Expand Down Expand Up @@ -215,6 +225,7 @@ type PreAggJobStatusResponse =
| PreAggJobStatusObject;

export {
ConvertQuery,
RequestContext,
RequestExtension,
ExtendedRequestContext,
Expand All @@ -228,6 +239,7 @@ export {
BaseRequest,
RequestQuery,
QueryRequest,
QueryConvertRequest,
PreAggsJobsRequest,
PreAggsSelector,
PreAggJob,
Expand Down
13 changes: 13 additions & 0 deletions packages/cubejs-backend-native/js/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,15 @@ export type Sql4SqlCommon = {
pushdown: boolean;
}
};

export type Sql4SqlResponse = Sql4SqlCommon & (Sql4SqlOk | Sql4SqlError);

export type QueryConvertResponse = {
status: string;
query: any;
error?: string;
};

let loadedNative: any = null;

export function loadNative() {
Expand Down Expand Up @@ -451,6 +458,12 @@ export const sql4sql = async (instance: SqlInterfaceInstance, sqlQuery: string,
return native.sql4sql(instance, sqlQuery, disablePostProcessing, securityContext ? JSON.stringify(securityContext) : null);
};

export const rest4sql = async (instance: SqlInterfaceInstance, sqlQuery: string, securityContext?: unknown): Promise<QueryConvertResponse> => {
const native = loadNative();

return native.rest4sql(instance, sqlQuery, securityContext ? JSON.stringify(securityContext) : null);
};

export const buildSqlAndParams = (cubeEvaluator: any): any[] => {
const native = loadNative();
return native.buildSqlAndParams(cubeEvaluator);
Expand Down
1 change: 1 addition & 0 deletions packages/cubejs-backend-native/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub mod node_obj_serializer;
pub mod orchestrator;
#[cfg(feature = "python")]
pub mod python;
pub mod rest4sql;
pub mod sql4sql;
pub mod stream;
pub mod template;
Expand Down
2 changes: 2 additions & 0 deletions packages/cubejs-backend-native/src/node_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use crate::config::{NodeConfiguration, NodeConfigurationFactoryOptions, NodeCube
use crate::cross::CLRepr;
use crate::cubesql_utils::with_session;
use crate::logger::NodeBridgeLogger;
use crate::rest4sql::rest4sql;
use crate::sql4sql::sql4sql;
use crate::stream::OnDrainHandler;
use crate::tokio_runtime_node;
Expand Down Expand Up @@ -788,6 +789,7 @@ pub fn register_module_exports<C: NodeConfiguration + 'static>(
cx.export_function("shutdownInterface", shutdown_interface)?;
cx.export_function("execSql", exec_sql)?;
cx.export_function("sql4sql", sql4sql)?;
cx.export_function("rest4sql", rest4sql)?;
cx.export_function("isFallbackBuild", is_fallback_build)?;
cx.export_function("__js_to_clrepr_to_js", debug_js_to_clrepr_to_js)?;

Expand Down
Loading
Loading