diff --git a/docs-mintlify/CLAUDE.md b/docs-mintlify/CLAUDE.md index 96d0a28b5bd3f..f06f9ab5f9b18 100644 --- a/docs-mintlify/CLAUDE.md +++ b/docs-mintlify/CLAUDE.md @@ -124,6 +124,7 @@ Make sure to use correct terms. On billing, pricing, and support pages, use **on - Controls - Filter - Time granularity + - Parent - AI summary - Layout - Spacer diff --git a/docs-mintlify/docs/explore-analyze/dashboards/widgets/charts.mdx b/docs-mintlify/docs/explore-analyze/dashboards/widgets/charts.mdx index 64a5449d3b273..67a86aa6f28fe 100644 --- a/docs-mintlify/docs/explore-analyze/dashboards/widgets/charts.mdx +++ b/docs-mintlify/docs/explore-analyze/dashboards/widgets/charts.mdx @@ -13,7 +13,7 @@ If the picker is empty, create the report first in a workbook tab — only publi ## Interaction with controls -Charts respect [controls][ref-controls] — filters and time granularity switchers — placed on the same dashboard. A single control can drive multiple charts at once: its value is applied to every chart whose query references the targeted dimension. +Charts respect the [controls][ref-controls] placed on the same dashboard — filters and time granularity switchers. A single control can drive multiple charts at once: its value is applied to every chart whose query references the targeted dimension. If some controls are incompatible with a chart's query, the chart skips them and shows a warning icon. See [Incompatible controls][ref-incompatible-controls] for details. diff --git a/docs-mintlify/docs/explore-analyze/dashboards/widgets/controls.mdx b/docs-mintlify/docs/explore-analyze/dashboards/widgets/controls.mdx index e3376efdba1bc..24c6e0f6eb3bf 100644 --- a/docs-mintlify/docs/explore-analyze/dashboards/widgets/controls.mdx +++ b/docs-mintlify/docs/explore-analyze/dashboards/widgets/controls.mdx @@ -1,16 +1,19 @@ --- title: Controls -description: Filter and time granularity switcher widgets that let dashboard viewers change what's shown on the dashboard. +description: Filter, time granularity switcher, and parent widgets that let dashboard viewers change what's shown on the dashboard. --- -Controls are widgets that let dashboard viewers change what's shown without leaving the dashboard. The dashboard builder offers two control types — both target a dimension from your semantic model and apply the selected value to every [chart][ref-charts] on the dashboard whose query references that dimension. +Controls are widgets that let dashboard viewers change what's shown without leaving the dashboard. The dashboard builder offers three control types: - [Filter](#filter) — Narrow the data shown on the dashboard - [Time granularity switcher](#time-granularity-switcher) — Change the granularity of time-based dimensions +- [Parent](#parent) — Re-point several other controls at once from a single dropdown + +Filters and time granularity switchers each target a dimension from your semantic model, and apply the selected value to every [chart][ref-charts] on the dashboard whose query references that dimension. A parent control works one level up: it targets no dimension of its own and drives *other controls* instead. ## Filter -Filter widgets let viewers narrow down the data shown on the dashboard. In the [dashboard builder][ref-workbooks], click **Filter** under **Add Controls** in the toolbar. The new filter is added in an unconfigured state — click **Configure Filter** (or open the widget's settings menu) to pick a semantic view and a dimension. +Filter widgets let viewers narrow down the data shown on the dashboard. In the [dashboard builder][ref-workbooks], open the **Add Controls** menu in the toolbar and choose **Filter**. The new filter is added in an unconfigured state — click **Configure Filter** (or open the widget's settings menu) to pick a semantic view and a dimension. ### Operators by dimension type @@ -78,7 +81,7 @@ For example, on a sales dashboard with a **Country** filter and a **City** filte Time granularity switchers let viewers change the granularity of time-based dimensions on the dashboard — for example, switching a revenue chart from daily to weekly or monthly. The widget targets a single time dimension and applies the chosen [granularity][ref-granularities] to every chart that groups by that dimension. -In the [dashboard builder][ref-workbooks], click **Time Granularity** under **Add Controls** in the toolbar. The new switcher is added in an unconfigured state — open its settings to pick a semantic view and a time dimension. +In the [dashboard builder][ref-workbooks], open the **Add Controls** menu in the toolbar and choose **Time Granularity**. The new switcher is added in an unconfigured state — open its settings to pick a semantic view and a time dimension. ### Allowed granularities @@ -92,25 +95,83 @@ Custom granularities defined in the [data model][ref-granularities] aren't offer You can configure a default granularity that's applied when the dashboard loads. If no default is set, charts use the granularity that was saved on the underlying report — viewers can still switch granularities, but the dashboard opens with each chart at its original granularity. +## Parent + +A parent control is a dropdown of options you define. Picking one re-points a whole row of other controls at once — so a viewer makes a single choice instead of adjusting three or four filters by hand. + +Unlike the other two control types, a parent control targets no dimension and never touches a chart query directly. It applies values to the controls it *drives* — its **children** — and those children then apply themselves to charts exactly as if the viewer had operated each one. Filters and time granularity switchers can both be children; a parent control cannot be a child of another parent control. + +For example, an **Analysis** parent with the options `Retail`, `Wholesale` and `Promo` can set a **Channel** filter, a **Minimum order value** filter, and a **Date range** filter to a different combination for each option. Viewers see one dropdown; you can [hide](#visibility) the children if the individual values aren't worth showing. + +In the [dashboard builder][ref-workbooks], open the **Add Controls** menu in the toolbar and choose **Parent**, then click **Configure Parent** to set it up. The editor has two tabs — **Options** and **Children**. + + +Add the child controls to the dashboard *before* the parent control. The **Children** tab can only map controls that already exist, so a parent added to an empty dashboard has nothing to drive yet. + + +### Options + +On the **Options** tab, type a label and click **Add** for each entry you want in the dropdown. Options appear as chips — remove one with its close button. A parent control can hold up to 50 options. + +Renaming an option later doesn't disturb the values you've mapped to it, so you can reword a label without redoing the mapping. + +### Children + +On the **Children** tab, pick a control from **Child control**, then give each of the parent's options a value for it. Each row renders *that child's own control* — a time granularity switcher's row shows its granularity picker, limited to the granularities that switcher allows; a filter's row shows that filter's operator and value inputs. So the values you can offer are exactly the ones a viewer could pick in the child itself. + +Repeat for each control you want the parent to drive. Every option/child pair can be in one of three states: + +| State | What happens when the viewer picks that option | +|---|---| +| **A value** | The child is set to that value. | +| **Reset to default** | The child is cleared back to its own default. For a filter that means no filtering on that dimension. Turn on the row's **Reset to default** switch. | +| **Left empty** | The child is left alone — it keeps whatever value the viewer already had. Use this deliberately when an option shouldn't have an opinion about a particular child. | + +While the parent's settings are open, the children it drives are highlighted on the canvas, so you can see the scope of the mapping at a glance. + +### One parent per child + +A control can be driven by only one parent control at a time. Mapping a child that another parent already drives **moves** it rather than sharing it — the editor warns you before you save, naming the parent that currently owns it. + +### Mapping status on child controls + +Once a dashboard has at least one parent control, every filter and time granularity switcher on it shows a small indicator reporting how it's driven: + +| Status | Meaning | +|---|---| +| **Fully driven** | Every option of the owning parent sets this control. | +| **Partly driven** | Only some of the owning parent's options set this control; the rest leave it alone. | +| **Not driven** | No parent control maps this one. | + +Click the indicator to jump straight to the **Children** tab of the parent that owns that control, with it already selected. For a control nothing drives yet, the click opens the first parent control on the dashboard — topmost, then leftmost — so you can map it. + +### Default option + +Set **Default value** to the option that should be selected when the dashboard loads. Choosing a default in the builder also applies that option's values to the children, so their saved defaults line up with the parent's and a published dashboard opens in a consistent state. + +If you leave **Default value** empty, the parent opens with nothing selected and the children use their own defaults. + ## Visibility -Each control has a **Visibility** setting that determines how it appears on the published dashboard. The setting applies to both filters and time granularity switchers. +Each control has a **Visibility** setting that determines how it appears on the published dashboard. The setting applies to all three control types. | Visibility | Behavior on the published dashboard | |---|---| | **Visible** (default) | Shown on the dashboard and viewers can change its value. | -| **Hidden** | Not shown to viewers, but the control's value is still applied to the charts it targets. Use this to scope a dashboard with a fixed value — e.g., always filter to the current quarter — without exposing the control. | +| **Hidden** | Not shown to viewers, but the control's value is still applied to the charts it targets. Use this to scope a dashboard with a fixed value — e.g., always filter to the current quarter — without exposing the control. Hiding the *children* of a [parent control](#parent) is the usual way to present one dropdown instead of the row of controls behind it. | | **Disabled** | Shown on the dashboard so viewers can see the active value, but they cannot change it. | Set the visibility from the **Visibility** dropdown when editing the control. **Hidden** controls remain visible in the dashboard builder so editors can reconfigure them, but disappear from the published view. ## Interaction with charts +This section applies to filters and time granularity switchers. A [parent control](#parent) has no dimension and never applies to a chart itself, so it doesn't appear in any chart's [Controls mapping](#controls-mapping) — it acts only through the children it drives, and it's those children that show up here. + When a control is added to a dashboard, it's automatically wired up to every [chart][ref-charts] whose query already uses the same dimension. Charts that don't reference that dimension are left alone, so a dashboard can mix scoped and unscoped views by default. You can override this default per chart from its [Controls mapping](#controls-mapping) — disable the control for that chart, or remap it onto a different dimension. ### Incompatible controls -If controls of a certain type are incompatible with a particular chart's query, the chart skips all controls of that type and renders the data without them. Controls of the other type still apply — for example, if filters fail but a time granularity switcher works, the chart shows the granularity-adjusted data without filtering, and vice versa. +If controls of a certain type are incompatible with a particular chart's query, the chart skips all controls of that type and renders the data without them. Filters and time granularity switchers are skipped independently — if filters fail but a time granularity switcher works, the chart shows the granularity-adjusted data without filtering, and vice versa. The chart displays a warning icon to indicate the problem: diff --git a/docs-mintlify/docs/explore-analyze/dashboards/widgets/index.mdx b/docs-mintlify/docs/explore-analyze/dashboards/widgets/index.mdx index c11b8126a0bb6..afeac4e2a1ebc 100644 --- a/docs-mintlify/docs/explore-analyze/dashboards/widgets/index.mdx +++ b/docs-mintlify/docs/explore-analyze/dashboards/widgets/index.mdx @@ -11,19 +11,21 @@ The dashboard builder supports the following widget types: - [Charts](/docs/explore-analyze/dashboards/widgets/charts) — Visualize reports from your workbook - [Text](/docs/explore-analyze/dashboards/widgets/text) — Add titles, descriptions, and rich formatting in Markdown -- [Controls](/docs/explore-analyze/dashboards/widgets/controls) — Let viewers filter the data or switch the time granularity +- [Controls](/docs/explore-analyze/dashboards/widgets/controls) — Let viewers filter the data, switch the time granularity, or drive several controls at once - [AI summary](/docs/explore-analyze/dashboards/widgets/ai-summary) — Generate narrative summaries of dashboard data on demand - [Spacer & Divider](/docs/explore-analyze/dashboards/widgets/layout) — Non-data layout elements for whitespace and section breaks (in preview) ## Adding widgets -Add widgets from the toolbar at the top of the dashboard builder: pick reports from the **Charts** picker to add charts, use the **Add Widgets** menu for text, AI summaries, and layout elements, or add a **Filter** or **Time Granularity** control from the **Add Controls** group. +Add widgets from the toolbar at the top of the dashboard builder: pick reports from the **Charts** picker to add charts, use the **Add Widgets** menu for text, AI summaries, and layout elements, or the **Add Controls** menu for **Filter**, **Time Granularity** and **Parent** controls. -Each toolbar item can be added in two ways: +Each item — a toolbar button, or an option inside the **Add Widgets** / **Add Controls** menus — can be added in two ways: - **Click** it to drop the widget into the first open spot on the canvas. - **Drag** it from the toolbar onto the canvas to place it exactly where you want. As you drag, a full-size placeholder previews the widget's footprint and the surrounding widgets reflow to open a slot; release to drop it there. Dragging is especially handy on dense dashboards, where clicking would otherwise place the new widget far down the page. +Starting a drag from a menu option closes the menu, so it doesn't cover the canvas while you place the widget. + Dragging a toolbar item to place it exactly (drag-to-place) is currently in preview, and the behavior may still change. Reach out to the [Cube support team](/admin/account-billing/support) to activate it for your account. Clicking to add a widget is available to everyone. diff --git a/docs-mintlify/docs/explore-analyze/workbooks/index.mdx b/docs-mintlify/docs/explore-analyze/workbooks/index.mdx index 75421288a2d9a..390915ad0af6b 100644 --- a/docs-mintlify/docs/explore-analyze/workbooks/index.mdx +++ b/docs-mintlify/docs/explore-analyze/workbooks/index.mdx @@ -129,8 +129,21 @@ and paste it as a new tab in any workbook using the same workflow. ## Duplicating workbooks You can duplicate a workbook by selecting **Duplicate** from the row -actions menu on the workspace page. This creates a full copy of the -workbook, including all its tabs, reports, and any published dashboard. +actions menu on the workspace page, from the workbook menu while the +workbook is open, or from a published dashboard's options menu. This +creates a full copy of the workbook, including all its tabs, reports, and +any published dashboard. + +A duplicate does not carry the original's sharing over by default. The +copy is visible only to you, plus anyone with access to the folder it is +created in. + +If the original is shared — with people, with groups, with your whole +organization, or through signed embedding — the duplicate dialog offers +**Copy sharing and embedding settings**. Selecting it gives the copy the +same audience. The option appears only if you have **Full access** to the +original, since copying its sharing means granting that access again. See +[Duplicates][ref-sharing-duplicates] for what does and doesn't carry over. ## Workbook versions @@ -151,4 +164,5 @@ has not been published will be lost. [ref-dashboards]: /docs/explore-analyze/dashboards +[ref-sharing-duplicates]: /docs/organize-content/sharing#duplicates diff --git a/docs-mintlify/docs/organize-content/sharing.mdx b/docs-mintlify/docs/organize-content/sharing.mdx index 192ba413f8c95..f8b14b3fe282e 100644 --- a/docs-mintlify/docs/organize-content/sharing.mdx +++ b/docs-mintlify/docs/organize-content/sharing.mdx @@ -141,6 +141,23 @@ This ensures users can always navigate to content they have permission to view, even if they don't have explicit access to every folder along the path. +### Duplicates + +Duplicating a workbook or dashboard does not carry its sharing over by +default — the copy starts visible only to you, plus anyone with access to +the folder it is created in. + +When the original is shared, the duplicate dialog offers **Copy sharing +and embedding settings**. Selecting it re-grants the original's users, +groups, and organization-wide access on the copy, and carries over +[signed embedding][ref-signed-embedding] if it was enabled. Access that +the original inherits from a folder is not copied — the duplicate derives +it from the folder it is created in, like any other new content there. + +The option requires **Full access** to the original, since copying its +sharing means granting that access again. With **Can edit** or +**Can view**, duplicating still works and the copy starts unshared. + ## Sharing folders Sharing a folder is the most efficient way to manage access for a @@ -175,3 +192,4 @@ interface. [ref-roles]: /admin/users-and-permissions/roles-and-permissions [ref-google-sheets]: /docs/integrations/google-sheets [ref-excel]: /docs/integrations/microsoft-excel +[ref-signed-embedding]: /embedding/iframe/auth/signed diff --git a/packages/cubejs-query-orchestrator/src/orchestrator/PreAggregations.ts b/packages/cubejs-query-orchestrator/src/orchestrator/PreAggregations.ts index df5015a4a957f..8c2e824bfb77f 100644 --- a/packages/cubejs-query-orchestrator/src/orchestrator/PreAggregations.ts +++ b/packages/cubejs-query-orchestrator/src/orchestrator/PreAggregations.ts @@ -468,9 +468,10 @@ export class PreAggregations { tables = tables.filter(row => `${schema}.${row.table_name}` === table); // fetching query result - const conn = await this.queue[dataSource].getQueueDriver().createConnection(); + const queue = await this.getQueue(dataSource); + const conn = await queue.getQueueDriver().createConnection(); const result = await conn.getResult(key); - this.queue[dataSource].getQueueDriver().release(conn); + queue.getQueueDriver().release(conn); // calculating status let status: string; diff --git a/packages/cubejs-query-orchestrator/test/unit/PreAggregations.test.ts b/packages/cubejs-query-orchestrator/test/unit/PreAggregations.test.ts index e63d6a54b8f31..17fd28ffa6e17 100644 --- a/packages/cubejs-query-orchestrator/test/unit/PreAggregations.test.ts +++ b/packages/cubejs-query-orchestrator/test/unit/PreAggregations.test.ts @@ -280,6 +280,38 @@ describe('PreAggregations', () => { }); }); + describe('isPartitionExist', () => { + test('initializes a missing data source queue before checking the job result', async () => { + const preAggregations = new PreAggregations( + 'TEST', + mockDriverFactory as any, + // eslint-disable-next-line @typescript-eslint/no-empty-function + () => {}, + queryCache!, + { + cacheAndQueueDriver: 'memory', + queueOptions: async () => ({ + executionTimeout: 1, + concurrency: 2, + }), + }, + ); + mockDriver!.tables.push('stb_pre_aggregations.orders_main'); + + await expect( + preAggregations.isPartitionExist( + 'request-id', + false, + 'named_data_source', + 'stb_pre_aggregations', + 'stb_pre_aggregations.orders_main', + ['job-key'], + 'job-token', + ) + ).resolves.toEqual([true, 'done']); + }); + }); + describe('loadAllPreAggregationsIfNeeded', () => { let preAggregations: PreAggregations | null = null;