Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4a460e5
feat: implement links feature for dimensions in the data model
cursoragent May 28, 2026
f8a6342
fix: address Claude review findings
cursoragent May 28, 2026
eed932b
fix: allow any string for param key, escape it to prevent injection
cursoragent May 28, 2026
47bb6d5
fix(lint): wrap ternary in parens to satisfy no-confusing-arrow rule
cursoragent May 28, 2026
16607a7
fix: address remaining review findings
cursoragent May 29, 2026
dccbf08
fix: remove duplicate closing brace from rebase conflict resolution
cursoragent May 30, 2026
8ba56f9
fix: URL-encode param keys at compile time
cursoragent May 30, 2026
9868be9
fix: remove unnecessary synthetic exception from view ownership check
cursoragent May 30, 2026
aef571c
test: add smoke test for params with dimension values in rendered URL
cursoragent May 30, 2026
e2bbf4a
test: add REST SQL API test for link synthetic dimensions
cursoragent May 30, 2026
75a03e5
fix: update smoke test for 3 links and handle cubesql chunked response
cursoragent May 30, 2026
0e2fef9
fix: resolve param value SQL from dimension definitions at compile time
cursoragent May 30, 2026
a327e4a
fix: relax params-through-view test assertion
cursoragent May 30, 2026
71a2401
test: add URL encoding verification test for param values with specia…
cursoragent May 30, 2026
c7ea43b
test: add smoke test verifying URL-encoded param values in query results
cursoragent May 30, 2026
06128e4
fix: revert to REPLACE chain for params URL encoding
cursoragent May 30, 2026
cacb36c
fix: use SQL_UTILS.urlEncode for params via compiler argument injection
cursoragent May 31, 2026
065f0e4
test: add smoke test for url link combined with params
cursoragent May 31, 2026
a0b7e64
fix: use dashboard instead of url with quotes in smoke test fixture
cursoragent May 31, 2026
e95c802
feat: add primary flag to links
cursoragent May 31, 2026
64d344c
fix: escape backslashes in valSql for CodeQL safety
cursoragent May 31, 2026
66e3335
fix: update symbols definition when upgrading synthetic dim with params
cursoragent May 31, 2026
e9c9c95
fix: resolve param values through symbol resolver like mask.sql
cursoragent May 31, 2026
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
4 changes: 4 additions & 0 deletions .github/actions/smoke.sh
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,7 @@ echo "::endgroup::"
echo "::group::RBAC GraphQL"
yarn lerna run --concurrency 1 --stream --no-prefix smoke:rbac-graphql
echo "::endgroup::"

echo "::group::Links"
yarn lerna run --concurrency 1 --stream --no-prefix smoke:links
echo "::endgroup::"
5 changes: 3 additions & 2 deletions docs-mintlify/reference/data-modeling/context-variables.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ values from the Cube query during SQL generation.

This is useful for hinting your database optimizer to use a specific index
or filter out partitions or shards in your cloud data warehouse so you won't
be billed for scanning those.
be billed for scanning those. It can also be useful for constructing [links][ref-links].

<Warning>

Expand Down Expand Up @@ -816,4 +816,5 @@ cube(`orders`, {
[ref-dynamic-data-models]: /docs/data-modeling/dynamic/jinja
[ref-query-filter]: /reference/rest-api/query-format#query-properties
[ref-dynamic-jinja]: /docs/data-modeling/dynamic/jinja
[ref-filter-boolean]: /reference/rest-api/query-format#boolean-logical-operators
[ref-filter-boolean]: /reference/rest-api/query-format#boolean-logical-operators
[ref-links]: /reference/data-modeling/dimensions#links
116 changes: 114 additions & 2 deletions docs-mintlify/reference/data-modeling/dimensions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -426,9 +426,107 @@ Using it with other dimension types will result in a validation error.

</Info>

### `links`

The `links` parameter allows you to define links associated with a dimension.
They can be rendered as HTML links by supporting tools, e.g., [Workbooks][ref-workbooks].

Links are useful to let users navigate to related external resources (e.g., Google
search), internal tools (e.g., Salesforce), or other pages in a BI tool.

Each link must have a `name` and a `label`. The `name` is used as an identifier
in the [synthetic dimension](#synthetic) name.

A link must specify either a `url` or a `dashboard`:
- `url` is a SQL expression that constructs the link URL. It can [reference][ref-references]
column and dimension values, just like the [`sql` parameter](#sql) or [`mask` parameter](#mask).
- `dashboard` is a dashboard identifier. When set, the link URL is generated as
`/dashboard/<dashboard_id>`. The `params` object is still appended as a query string.

Optionally, a link might use the `icon` parameter to reference an icon from a [supported
icon set][link-tabler] to be displayed alongside the link label.

Optionally, a link might use the `target` parameter to specify [where to open it][link-target]:
`blank` (default) to open in a new tab/window or `self` to open in the same tab/window.

```yaml
cubes:
- name: users

dimensions:
# Definitions of dimensions such as `email`, etc.

- name: full_name
sql: full_name
type: string
links:
- name: google_search
label: Search on Google
url: "CONCAT('https://www.google.com/search?q=', {CUBE}.full_name)"
icon: brand-google
target: blank

- name: salesforce_search
label: Search in Salesforce
url: "CONCAT('https://your-company.salesforce.com/search/results/?q=', {email})"
target: blank

- name: send_email
label: Write an email
url: "CONCAT('mailto:', {email})"
icon: send
```

#### `params`

The optional `params` parameter can be used to add additional query parameters to the
URL. It accepts a map of key-value pairs, where keys are parameter names and values are
SQL expressions (just like `url`).

Values in `params` can [reference][ref-references] columns and dimension values.
All parameter values will be [URL-encoded][link-encode-uri-component] in the generated SQL
using a database-specific encoding function.

```yaml
cubes:
- name: users

dimensions:
# Definitions of dimensions such as `id`, `country`, etc.

- name: full_name
sql: full_name
type: string
links:
- name: performance
label: Check performance dashboard
dashboard: KSqDYdUz6Ble
params:
- key: filter_user_id
value: "{id}"
- key: filter_country
value: "{country}"
```

#### Synthetic dimensions

Each link will be rendered as an additional [synthetic](#synthetic) dimension in the
result set, with the following naming convention:

- `<dimension_name>___link_<link_name>_url`

<Info>

All references in link URLs and parameters must resolve to a single value for a given
value of the dimension on which the link is defined. Otherwise, it will result in
duplicate rows in the result set.

</Info>

### `meta`

Custom metadata. Can be used to pass any information to the frontend.
The `meta` parameter allows you to attach arbitrary information to a dimension.
It can be consumed and interpreted by supporting tools.

You can also use the `ai_context` key to provide context to the
[AI agent][ref-ai-context] without exposing it in the user interface.
Expand Down Expand Up @@ -902,6 +1000,13 @@ cube(`orders`, {

</CodeGroup>

### `synthetic`

The `synthetic` parameter can't be set by a user directly. It is used to mark dimensions
that are automatically created by Cube, e.g., for [links](#links).

You can check if a dimension is synthetic via the [`/v1/meta` API endpoint][ref-meta-api].

### `granularities`

By default, the following granularities are available for time dimensions:
Expand Down Expand Up @@ -1222,4 +1327,11 @@ cube(`fiscal_calendar`, {
[link-tesseract]: https://cube.dev/blog/introducing-next-generation-data-modeling-engine
[ref-case-measures]: /reference/data-modeling/measures#case
[ref-meta-api]: /reference/rest-api/reference#base_pathv1meta
[ref-string-time-dims]: /recipes/data-modeling/string-time-dimensions
[ref-string-time-dims]: /recipes/data-modeling/string-time-dimensions
[ref-workbooks]: /docs/explore-analyze/workbooks
[link-target]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/a#target
[link-tabler]: https://tabler.io/icons
[ref-references]: /docs/data-modeling/syntax#references
[ref-filter-params]: /reference/data-modeling/context-variables#filter_params
[link-encode-uri-component]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
[ref-rest-filters]: /reference/rest-api/query-format#query-properties
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ values from the Cube query during SQL generation.

This is useful for hinting your database optimizer to use a specific index
or filter out partitions or shards in your cloud data warehouse so you won't
be billed for scanning those.
be billed for scanning those. It can also be useful for constructing [links][ref-links].

<WarningBox>

Expand Down Expand Up @@ -816,4 +816,5 @@ cube(`orders`, {
[ref-dynamic-data-models]: /product/data-modeling/dynamic/jinja
[ref-query-filter]: /product/apis-integrations/rest-api/query-format#query-properties
[ref-dynamic-jinja]: /product/data-modeling/dynamic/jinja
[ref-filter-boolean]: /product/apis-integrations/rest-api/query-format#boolean-logical-operators
[ref-filter-boolean]: /product/apis-integrations/rest-api/query-format#boolean-logical-operators
[ref-links]: /product/data-modeling/reference/dimensions#links
115 changes: 114 additions & 1 deletion docs/content/product/data-modeling/reference/dimensions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,107 @@ cubes:

</CodeTabs>

### `links`

The `links` parameter allows you to define links associated with a dimension.
They can be rendered as HTML links by supporting tools, e.g., [Workbooks][ref-workbooks].

Links are useful to let users navigate to related external resources (e.g., Google
search), internal tools (e.g., Salesforce), or other pages in a BI tool.

Each link must have a `name` and a `label`. The `name` is used as an identifier
in the [synthetic dimension](#synthetic) name.

A link must specify either a `url` or a `dashboard`:
- `url` is a SQL expression that constructs the link URL. It can [reference][ref-references]
column and dimension values, just like the [`sql` parameter](#sql) or [`mask` parameter](#mask).
- `dashboard` is a dashboard identifier. When set, the link URL is generated as
`/dashboard/<dashboard_id>`. The `params` object is still appended as a query string.

Optionally, a link might use the `icon` parameter to reference an icon from a [supported
icon set][link-tabler] to be displayed alongside the link label.

Optionally, a link might use the `target` parameter to specify [where to open it][link-target]:
`blank` (default) to open in a new tab/window or `self` to open in the same tab/window.

```yaml
cubes:
- name: users

dimensions:
# Definitions of dimensions such as `email`, etc.

- name: full_name
sql: full_name
type: string
links:
- name: google_search
label: Search on Google
url: "CONCAT('https://www.google.com/search?q=', {CUBE}.full_name)"
icon: brand-google
target: blank

- name: salesforce_search
label: Search in Salesforce
url: "CONCAT('https://your-company.salesforce.com/search/results/?q=', {email})"
target: blank

- name: send_email
label: Write an email
url: "CONCAT('mailto:', {email})"
icon: send
```

#### `params`

The optional `params` parameter can be used to add additional query parameters to the
URL. It accepts a map of key-value pairs, where keys are parameter names and values are
SQL expressions (just like `url`).

Values in `params` can [reference][ref-references] columns and dimension values.
All parameter values will be [URL-encoded][link-encode-uri-component] in the generated SQL
using a database-specific encoding function.

```yaml
cubes:
- name: users

dimensions:
# Definitions of dimensions such as `id`, `country`, etc.

- name: full_name
sql: full_name
type: string
links:
- name: performance
label: Check performance dashboard
dashboard: KSqDYdUz6Ble
params:
- key: filter_user_id
value: "{id}"
- key: filter_country
value: "{country}"
```

#### Synthetic dimensions

Each link will be rendered as an additional [synthetic](#synthetic) dimension in the
result set, with the following naming convention:

- `<dimension_name>___link_<link_name>_url`

<InfoBox>

All references in link URLs and parameters must resolve to a single value for a given
value of the dimension on which the link is defined. Otherwise, it will result in
duplicate rows in the result set.

</InfoBox>

### `meta`

Custom metadata. Can be used to pass any information to the frontend.
The `meta` parameter allows you to attach arbitrary information to a dimension.
It can be consumed and interpreted by supporting tools.

<CodeTabs>

Expand Down Expand Up @@ -701,6 +799,13 @@ cubes:

</CodeTabs>

### `synthetic`

The `synthetic` parameter can't be set by a user directly. It is used to mark dimensions
that are automatically created by Cube, e.g., for [links](#links).

You can check if a dimension is synthetic via the [`/v1/meta` API endpoint][ref-meta].

### `granularities`

By default, the following granularities are available for time dimensions:
Expand Down Expand Up @@ -1015,3 +1120,11 @@ cube(`fiscal_calendar`, {
[ref-cube-calendar]: /product/data-modeling/reference/cube#calendar
[ref-measure-time-shift]: /product/data-modeling/reference/measures#time_shift
[ref-data-masking]: /product/auth/data-access-policies#data-masking
[ref-workbooks]: /product/exploration/workbooks
[link-target]: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/a#target
[link-tabler]: https://tabler.io/icons
[ref-references]: /product/data-modeling/syntax#references
[ref-filter-params]: /product/data-modeling/reference/context-variables#filter_params
[link-encode-uri-component]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
[ref-rest-filters]: /product/apis-integrations/rest-api/query-format#query-properties
[ref-meta]: /product/apis-integrations/rest-api/reference#base_pathv1meta
Original file line number Diff line number Diff line change
Expand Up @@ -216,4 +216,8 @@ export class DatabricksQuery extends BaseQuery {
delete templates.types.interval;
return templates;
}

public urlEncode(sql: string): string {
return `url_encode(CAST(${sql} as STRING))`;
}
}
18 changes: 16 additions & 2 deletions packages/cubejs-schema-compiler/src/adapter/BaseQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -3989,6 +3989,17 @@ export class BaseQuery {
throw new Error('Not implemented');
}

/**
* URL-encode a SQL expression. Override in dialect-specific query classes
* for native URL encoding support. Default implementation uses REPLACE
* chains for the most common characters.
* @param {string} sql
* @return {string}
*/
urlEncode(sql) {
return `REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(CAST(${sql} as TEXT), '%', '%25'), '&', '%26'), '=', '%3D'), '+', '%2B'), ' ', '%20')`;
}

/**
* @param {string} granularity
* @param {string} dimension
Expand Down Expand Up @@ -5007,7 +5018,8 @@ export class BaseQuery {
filterParams: this.filtersProxy(),
filterGroup: this.filterGroupFunction(),
sqlUtils: {
convertTz: this.convertTz.bind(this)
convertTz: this.convertTz.bind(this),
urlEncode: this.urlEncode.bind(this)
}
}, R.map(
(symbols) => this.contextSymbolsProxy(symbols),
Expand All @@ -5023,6 +5035,7 @@ export class BaseQuery {
filterGroup: () => '1 = 1',
sqlUtils: {
convertTz: (field) => field,
urlEncode: (sql) => sql,
},
securityContext: CubeSymbols.contextSymbolsProxyFrom({}, allocateParam),
};
Expand All @@ -5034,7 +5047,8 @@ export class BaseQuery {

sqlUtilsForRust() {
return {
convertTz: this.convertTz.bind(this)
convertTz: this.convertTz.bind(this),
urlEncode: this.urlEncode.bind(this)
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,8 @@ export class PrestodbQuery extends BaseQuery {
public castToString(sql: any): string {
return `CAST(${sql} as VARCHAR)`;
}

public urlEncode(sql: string): string {
return `url_encode(CAST(${sql} as VARCHAR))`;
}
}
Loading
Loading