Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rill Developer: add UI for external tables #4027

Merged
merged 24 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
fe0eea9
Add external table page and navigation
ericpgreen2 Feb 12, 2024
43d3eff
Use `table` not `model` in generated dashboard YAML
ericpgreen2 Feb 13, 2024
77e5d6d
Add "Autogenerate dashboard" action to header
ericpgreen2 Feb 13, 2024
fd35b1b
Add "Autogenerate dashboard" action to context menu
ericpgreen2 Feb 13, 2024
2e2f4ca
Hide modeling for Druid connections
ericpgreen2 Feb 13, 2024
776c779
Only show sources for DuckDB connections
ericpgreen2 Feb 13, 2024
9e8166a
Merge remote-tracking branch 'remotes/origin/main' into external-tabl…
nishantmonu51 Feb 13, 2024
56a5c95
Merge remote-tracking branch 'remotes/origin/main' into external-tabl…
nishantmonu51 Feb 13, 2024
ca21d98
Terminology: use "table" not "external table"
ericpgreen2 Feb 14, 2024
2377755
Remove modeling layer for clickhouse driver
ericpgreen2 Feb 14, 2024
f673884
Add a `default_time_range` of "P4W"
ericpgreen2 Feb 14, 2024
ca2721f
Differentiate between models and tables in generated dashboard code
ericpgreen2 Feb 14, 2024
4151995
Fix placeholder for OLAP drivers that don't support modeling
ericpgreen2 Feb 14, 2024
10ddda7
Use selector `isModelingSupportedForCurrentOlapDriver`
ericpgreen2 Feb 14, 2024
62387df
Bugfix
ericpgreen2 Feb 14, 2024
8504033
Add navigation overflow/scroll when many tables
ericpgreen2 Feb 14, 2024
661b0b1
Only route to Welcome page for DuckDB OLAP driver
ericpgreen2 Feb 14, 2024
7024e2c
Add different onboarding steps for non-DuckDB OLAP drivers
ericpgreen2 Feb 14, 2024
08caf0b
Edit copy
ericpgreen2 Feb 14, 2024
d3d597c
Fix test (and slight test refactor)
ericpgreen2 Feb 14, 2024
b29bcd7
Fix test
ericpgreen2 Feb 14, 2024
4491b3c
Fix lint
ericpgreen2 Feb 14, 2024
a274225
Filter out the managed tables (sources and models)
ericpgreen2 Feb 14, 2024
c7416e6
Merge branch 'main' into external-table-ui
ericpgreen2 Feb 16, 2024
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
19 changes: 15 additions & 4 deletions web-common/src/features/metrics-views/metrics-internal-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export function initBlankDashboardYAML(dashboardTitle: string) {
# Visit https://docs.rilldata.com/reference/project-files to learn more about Rill project files.

title: ""
model: ""
table: ""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we prefer table for the blank dashbaord?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because model only works for models, but table works for these external tables plus sources and models.

default_time_range: ""
smallest_time_grain: ""
timeseries: ""
Expand All @@ -66,10 +66,12 @@ available_time_zones:
return template.toString();
}

export function generateDashboardYAMLForModel(
modelName: string,
export function generateDashboardYAMLForTable(
tableName: string,
isModel: boolean,
schema: V1StructType,
dashboardTitle = "",
defaultTimeRange = "",
) {
const doc = new Document();

Expand All @@ -78,7 +80,12 @@ export function generateDashboardYAMLForModel(
if (dashboardTitle) {
doc.set("title", dashboardTitle);
}
doc.set("model", modelName);

if (isModel) {
doc.set("model", tableName);
} else {
doc.set("table", tableName);
}

const timestampColumns = selectTimestampColumnFromSchema(schema);
if (timestampColumns?.length) {
Expand All @@ -87,6 +94,10 @@ export function generateDashboardYAMLForModel(
doc.set("timeseries", "");
}

if (defaultTimeRange) {
doc.set("default_time_range", defaultTimeRange);
}

const fields = schema.fields;
const metricsSeq = fields
.filter((field) => {
Expand Down
18 changes: 17 additions & 1 deletion web-common/src/features/metrics-views/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
export function getModelOutOfPossiblyMalformedYAML(yaml: string): string {
export function getModelOutOfPossiblyMalformedYAML(
yaml: string,
): string | null {
// Regular expression to match model key followed by its value
// The regex looks for 'model:' followed by any number of whitespaces and captures any non-whitespace characters after that
const regex = /model:\s*(\S+)/;
Expand All @@ -9,3 +11,17 @@ export function getModelOutOfPossiblyMalformedYAML(yaml: string): string {
// If matches were found, return the value of the model field, otherwise return null
return matches && matches[1] ? matches[1] : null;
}

export function getTableOutOfPossiblyMalformedYAML(
yaml: string,
): string | null {
// Regular expression to match table key followed by its value
// The regex looks for 'table:' followed by any number of whitespaces and captures any non-whitespace characters after that
const regex = /table:\s*(\S+)/;

// Extract the match groups
const matches = regex.exec(yaml);

// If matches were found, return the value of the table field, otherwise return null
return matches && matches[1] ? matches[1] : null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import { waitForResource } from "@rilldata/web-common/features/entity-management/resource-status-utils";
import { EntityType } from "@rilldata/web-common/features/entity-management/types";
import {
generateDashboardYAMLForModel,
generateDashboardYAMLForTable,
initBlankDashboardYAML,
} from "@rilldata/web-common/features/metrics-views/metrics-internal-store";
import { useModelFileNames } from "@rilldata/web-common/features/models/selectors";
Expand All @@ -25,10 +25,13 @@
} from "@rilldata/web-common/runtime-client";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import { useQueryClient } from "@tanstack/svelte-query";
import { useIsModelingSupportedForCurrentOlapDriver } from "../../../tables/selectors";

export let metricsName: string;
export let view: EditorView | undefined = undefined;

$: isModelingSupportedForCurrentOlapDriver =
useIsModelingSupportedForCurrentOlapDriver($runtime.instanceId);
$: models = useModelFileNames($runtime.instanceId);

const queryClient = useQueryClient();
Expand Down Expand Up @@ -64,8 +67,9 @@
}),
});

const isModel = true;
const dashboardYAML = schemaResp?.schema
? generateDashboardYAMLForModel(modelName, schemaResp?.schema)
? generateDashboardYAMLForTable(modelName, isModel, schemaResp?.schema)
: "";

await runtimeServicePutFile(
Expand Down Expand Up @@ -128,40 +132,44 @@
</script>

<div class="whitespace-normal">
Auto-generate a <WithTogglableFloatingElement
distance={8}
inline
let:toggleFloatingElement
>
<button
class={buttonClasses}
disabled={!$models?.data?.length}
on:click={toggleFloatingElement}
>metrics configuration from an existing model</button
>,
<Menu
dark
on:click-outside={toggleFloatingElement}
on:escape={toggleFloatingElement}
slot="floating-element"
{#if $isModelingSupportedForCurrentOlapDriver.data}
Auto-generate a <WithTogglableFloatingElement
distance={8}
inline
let:toggleFloatingElement
>
{#each $models?.data ?? [] as model}
<MenuItem
on:select={() => {
onAutogenerateConfigFromModel(model);
toggleFloatingElement();
}}
>
{model}
</MenuItem>
{/each}
</Menu>
</WithTogglableFloatingElement>
<button
class={buttonClasses}
disabled={!$models?.data?.length}
on:click={toggleFloatingElement}
>metrics configuration from an existing model</button
>,
<Menu
dark
on:click-outside={toggleFloatingElement}
on:escape={toggleFloatingElement}
slot="floating-element"
let:toggleFloatingElement
>
{#each $models?.data ?? [] as model}
<MenuItem
on:select={() => {
onAutogenerateConfigFromModel(model);
toggleFloatingElement();
}}
>
{model}
</MenuItem>
{/each}
</Menu>
</WithTogglableFloatingElement>
{/if}
<button
class={buttonClasses}
on:click={async () => {
onCreateSkeletonMetricsConfig();
}}>start with a skeleton</button
}}
>{#if $isModelingSupportedForCurrentOlapDriver.data}s{:else}S{/if}tart with
a skeleton</button
>, or just start typing.
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
} from "@rilldata/web-common/runtime-client";
import { runtime } from "@rilldata/web-common/runtime-client/runtime-store";
import { slide } from "svelte/transition";
import { getModelOutOfPossiblyMalformedYAML } from "../../utils";
import {
getModelOutOfPossiblyMalformedYAML,
getTableOutOfPossiblyMalformedYAML,
} from "../../utils";

export let metricsDefName: string;

Expand All @@ -31,9 +34,10 @@

// get file.
$: modelName = getModelOutOfPossiblyMalformedYAML(yaml)?.replace(/"/g, "");
$: tableName = getTableOutOfPossiblyMalformedYAML(yaml)?.replace(/"/g, "");

// check to see if this model name exists.
$: modelQuery = useModel($runtime.instanceId, modelName);
$: modelQuery = useModel($runtime.instanceId, modelName ?? "");

$: allModels = useModels($runtime.instanceId);

Expand Down Expand Up @@ -113,6 +117,14 @@
to a model.
</p>
</div>
{:else if tableName !== undefined}
<div>
<p>Table not defined.</p>
<p>
Set a table with <code>table: TABLE_NAME</code> to connect your metrics
to a table.
</p>
</div>
{/if}
</div>
{/if}
Expand Down
12 changes: 7 additions & 5 deletions web-common/src/features/models/createDashboardFromModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import {
} from "@rilldata/web-common/features/entity-management/entity-mappers";
import { getName } from "@rilldata/web-common/features/entity-management/name-utils";
import {
createSchemaForTable,
ResourceKind,
createSchemaForTable,
useResource,
} from "@rilldata/web-common/features/entity-management/resource-selectors";
import { waitForResource } from "@rilldata/web-common/features/entity-management/resource-status-utils";
import { EntityType } from "@rilldata/web-common/features/entity-management/types";
import { generateDashboardYAMLForModel } from "@rilldata/web-common/features/metrics-views/metrics-internal-store";
import { generateDashboardYAMLForTable } from "@rilldata/web-common/features/metrics-views/metrics-internal-store";
import { appScreen } from "@rilldata/web-common/layout/app-store";
import { overlay } from "@rilldata/web-common/layout/overlay-store";
import { behaviourEvent } from "@rilldata/web-common/metrics/initMetrics";
Expand All @@ -24,15 +24,15 @@ import {
} from "@rilldata/web-common/metrics/service/MetricsTypes";
import {
RpcStatus,
runtimeServicePutFile,
V1ReconcileStatus,
V1StructType,
runtimeServicePutFile,
} from "@rilldata/web-common/runtime-client";
import {
createMutation,
CreateMutationOptions,
MutationFunction,
QueryClient,
createMutation,
useQueryClient,
} from "@tanstack/svelte-query";
import { derived, get } from "svelte/store";
Expand Down Expand Up @@ -65,8 +65,10 @@ export const useCreateDashboardFromModel = <
const { data } = props ?? {};

// create dashboard from model
const dashboardYAML = generateDashboardYAMLForModel(
const isModel = true;
const dashboardYAML = generateDashboardYAMLForTable(
data.modelName,
isModel,
data.schema,
data.newDashboardName,
);
Expand Down
68 changes: 51 additions & 17 deletions web-common/src/features/onboarding/OnboardingWorkspace.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@
import Button from "../../components/button/Button.svelte";
import Add from "../../components/icons/Add.svelte";
import { WorkspaceContainer } from "../../layout/workspace";
import { createRuntimeServiceGetInstance } from "../../runtime-client";
import { runtime } from "../../runtime-client/runtime-store";

let steps: OnboardingStep[];
$: instance = createRuntimeServiceGetInstance($runtime.instanceId);
$: olapConnector = $instance.data?.instance?.olapConnector;
$: if (olapConnector) {
steps = olapConnector === "duckdb" ? duckDbSteps : nonDuckDbSteps;
}

interface OnboardingStep {
id: string;
heading: string;
description: string;
}

const steps: OnboardingStep[] = [
// Onboarding steps for DuckDB OLAP driver
const duckDbSteps: OnboardingStep[] = [
{
id: "source",
heading: "Import your data source",
Expand All @@ -38,6 +48,28 @@
},
];

// Onboarding steps for non-DuckDB OLAP drivers (Clickhouse, Druid)
const nonDuckDbSteps: OnboardingStep[] = [
{
id: "table",
heading: "Explore your tables",
description:
"Find your database tables in the left-hand-side navigational sidebar.",
},
{
id: "metrics",
heading: "Define your metrics and dimensions",
description:
"Define aggregate metrics and break out dimensions for your tables.",
},
{
id: "dashboard",
heading: "Explore your metrics dashboard",
description:
"Interactively explore line charts and leaderboards to uncover insights.",
},
];

let showAddSourceModal = false;
function openAddSourceModal() {
showAddSourceModal = true;
Expand All @@ -53,23 +85,25 @@
<ol
class="max-w-fit flex flex-col gap-y-4 px-9 pt-9 pb-[60px] bg-gray-50 rounded-lg border border-gray-200"
>
{#each steps as step, i (step.heading)}
<li class="flex gap-x-0.5">
<span class="font-bold">{i + 1}.</span>
<div class="flex flex-col items-start gap-y-2">
<div class="flex flex-col items-start gap-y-0.5">
<h5 class="font-bold">{step.heading}</h5>
<p>{step.description}</p>
{#if olapConnector}
{#each steps as step, i (step.heading)}
<li class="flex gap-x-0.5">
<span class="font-bold">{i + 1}.</span>
<div class="flex flex-col items-start gap-y-2">
<div class="flex flex-col items-start gap-y-0.5">
<h5 class="font-bold">{step.heading}</h5>
<p>{step.description}</p>
</div>
{#if step.id === "source"}
<Button type="secondary" on:click={openAddSourceModal}>
<IconSpaceFixer pullLeft><Add /></IconSpaceFixer>
<span>Add data</span>
</Button>
{/if}
</div>
{#if step.id === "source"}
<Button type="secondary" on:click={openAddSourceModal}>
<IconSpaceFixer pullLeft><Add /></IconSpaceFixer>
<span>Add data</span>
</Button>
{/if}
</div>
</li>
{/each}
</li>
{/each}
{/if}
</ol>
<AddSourceModal
open={showAddSourceModal}
Expand Down
Loading
Loading