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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import Tooltip from "@rilldata/web-common/components/tooltip/Tooltip.svelte";
import TooltipContent from "@rilldata/web-common/components/tooltip/TooltipContent.svelte";
import { ResourceKind } from "@rilldata/web-common/features/entity-management/resource-selectors";
import { refreshableTypes } from "@rilldata/web-common/features/resources/resource-filter-utils";
import type { V1Resource } from "@rilldata/web-common/runtime-client";
import {
RefreshCcwIcon,
Expand All @@ -23,7 +24,7 @@
export let onClickRefreshDialog: (
resourceName: string,
resourceKind: string,
refreshType: "full" | "incremental",
refreshType: "refresh" | "full" | "incremental",
) => void;
export let onClickRefreshErroredPartitions: (resourceName: string) => void;
export let onClickViewSpec: (
Expand All @@ -39,8 +40,7 @@
export let onDropdownOpenChange: (isOpen: boolean) => void;

$: isModel = resourceKind === ResourceKind.Model;
$: isSource = resourceKind === ResourceKind.Source;
$: canRefresh = isModel || isSource;
$: canRefresh = refreshableTypes.includes(resourceKind as ResourceKind);

$: actions = isModel ? getAvailableModelActions(resource) : [];
$: isPartitioned = actions.includes("viewPartitions");
Expand Down Expand Up @@ -93,8 +93,7 @@
</DropdownMenu.Item>
{/if}

<!-- Refresh actions (models + sources) -->
{#if canRefresh}
{#if isModel}
<DropdownMenu.Separator />

<!-- Refresh Errored Partitions (models with errors) -->
Expand Down Expand Up @@ -153,6 +152,24 @@
>
</Tooltip>
{/if}
{:else if canRefresh}
<DropdownMenu.Separator />

<!-- Other resources only support a standard refresh. -->
<Tooltip distance={8} suppress={!refreshDisabled}>
<DropdownMenu.Item
class="font-normal flex items-center"
disabled={refreshDisabled}
onclick={() =>
onClickRefreshDialog(resourceName, resourceKind, "refresh")}
>
<div class="flex items-center">
<RefreshCcwIcon size="12px" />
<span class="ml-2">{m.status_action_refresh()}</span>
</div>
</DropdownMenu.Item>
<TooltipContent slot="tooltip-content">{refreshTooltip}</TooltipContent>
</Tooltip>
{/if}
</DropdownMenu.Content>
</DropdownMenu.Root>
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
let isConfirmDialogOpen = false;
let dialogResourceName = "";
let dialogResourceKind = "";
let dialogRefreshType: "full" | "incremental" = "full";
let dialogRefreshType: "refresh" | "full" | "incremental" = "refresh";

let isSpecDialogOpen = false;
let specResourceName = "";
Expand All @@ -61,7 +61,7 @@
const openRefreshDialog = (
resourceName: string,
resourceKind: string,
refreshType: "full" | "incremental",
refreshType: "refresh" | "full" | "incremental",
) => {
dialogResourceName = resourceName;
dialogResourceKind = resourceKind;
Expand Down
44 changes: 43 additions & 1 deletion web-admin/tests/project-status-refresh.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ test.describe("Project Status - Resource Refresh (openrtb)", () => {

test("should show Full Refresh option for sources", async ({ adminPage }) => {
// Wait for the source row to be visible before interacting
// Look for bids_data_raw source which should be in the openrtb test project
// Look for bids_data_raw source which should be in the openrtb test project.
// NOTE: `type: source` files are parsed into models (defined_as_source), so
// this row is a model and shows the full/incremental refresh actions.
await expect(adminPage.getByText("bids_data_raw")).toBeVisible({
timeout: 60_000,
});
Expand All @@ -61,6 +63,46 @@ test.describe("Project Status - Resource Refresh (openrtb)", () => {
).not.toBeVisible();
});

test("should show only Refresh for metrics views", async ({ adminPage }) => {
await expect(adminPage.getByText("auction_metrics")).toBeVisible({
timeout: 60_000,
});

const metricsViewRow = adminPage.locator(".row").filter({
hasText: "auction_metrics",
});
await metricsViewRow
.getByRole("button", { name: "Open resource actions" })
.click();

await expect(
adminPage.getByRole("menuitem", { name: "Refresh", exact: true }),
).toBeVisible();
await expect(
adminPage.getByRole("menuitem", { name: "Full Refresh" }),
).not.toBeVisible();
await expect(
adminPage.getByRole("menuitem", { name: "Incremental Refresh" }),
).not.toBeVisible();

await adminPage
.getByRole("menuitem", { name: "Refresh", exact: true })
.click();
await expect(adminPage.getByRole("alertdialog")).toBeVisible();
await expect(
adminPage.getByRole("heading", { name: /Refresh auction_metrics/ }),
).toBeVisible();
await expect(
adminPage.getByText(
"Refreshing this resource will update all dependent resources.",
),
).toBeVisible();
await expect(
adminPage.getByText(/Warning.*re-ingest ALL data/),
).toHaveCount(0);
await adminPage.getByRole("button", { name: "Cancel" }).click();
});

test("should not show Incremental Refresh for non-incremental models", async ({
adminPage,
}) => {
Expand Down
49 changes: 26 additions & 23 deletions web-common/src/features/projects/status/ActionsCell.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -29,32 +29,33 @@

$: isLoading = $triggerMutation.isPending;

$: isModel = resourceKind === ResourceKind.Model;
$: supportsIncremental =
resourceKind === ResourceKind.Model &&
resource?.model?.spec?.incremental === true;
isModel && resource?.model?.spec?.incremental === true;

async function handleRefresh(refreshType: "full" | "incremental") {
async function handleRefresh(
refreshType: "refresh" | "full" | "incremental",
) {
if (isLoading) return;

try {
const body =
resourceKind === ResourceKind.Model
? {
models: [
{
model: resourceName,
full: refreshType === "full",
},
],
}
: {
resources: [
{
kind: resourceKind,
name: resourceName,
},
],
};
const body = isModel
? {
models: [
{
model: resourceName,
full: refreshType === "full",
},
],
}
: {
resources: [
{
kind: resourceKind,
name: resourceName,
},
],
};

await $triggerMutation.mutateAsync(body);

Expand Down Expand Up @@ -89,15 +90,17 @@
class="font-normal flex items-center"
disabled={isLoading}
onclick={() => {
handleRefresh("full");
handleRefresh(isModel ? "full" : "refresh");
}}
>
<div class="flex items-center">
<RefreshCcwIcon size="12px" />
<span class="ml-2"
>{isLoading
? m.status_refreshing()
: m.status_action_full_refresh()}</span
: isModel
? m.status_action_full_refresh()
: m.status_action_refresh()}</span
>
</div>
</DropdownMenu.Item>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
export let open = false;
export let name: string;
export let onRefresh: () => Promise<void> | void;
export let refreshType: "full" | "incremental" = "full";
export let refreshType: "refresh" | "full" | "incremental" = "full";

let isRefreshing = false;

Expand Down Expand Up @@ -45,15 +45,19 @@
<AlertDialogTitle>
{refreshType === "full"
? m.status_action_full_refresh()
: m.status_action_incremental_refresh()}
: refreshType === "incremental"
? m.status_action_incremental_refresh()
: m.status_action_refresh()}
<span class="font-semibold" title={name}>{truncateName(name)}</span>?
</AlertDialogTitle>
<AlertDialogDescription>
<div class="mt-1">
{#if refreshType === "full"}
{m.status_full_refresh_warning()}
{:else}
{:else if refreshType === "incremental"}
{m.status_incremental_refresh_description()}
{:else}
{m.status_refresh_description()}
{/if}
</div>
</AlertDialogDescription>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
filterableTypes,
filterResources,
getStatusPriority,
refreshableTypes,
statusFilters,
} from "@rilldata/web-common/features/resources/resource-filter-utils";
import ActionsCell from "@rilldata/web-common/features/projects/status/ActionsCell.svelte";
Expand Down Expand Up @@ -194,8 +195,9 @@
resourceName: row.original.meta?.name?.name ?? "",
canRefresh:
!isRowReconciling &&
(row.original.meta?.name?.kind === ResourceKind.Model ||
row.original.meta?.name?.kind === ResourceKind.Source),
refreshableTypes.includes(
row.original.meta?.name?.kind as ResourceKind,
),
resource: row.original,
onRefresh: onRefetch,
onDescribe: handleDescribe,
Expand Down
13 changes: 13 additions & 0 deletions web-common/src/features/resources/resource-filter-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ export const filterableTypes = [
ResourceKind.Connector,
];

// Resource kinds that support a manual refresh trigger from the resource listing.
// Excludes Alert and Report: triggering those executes them and dispatches notifications to recipients.
export const refreshableTypes = [
ResourceKind.Source,
ResourceKind.Model,
ResourceKind.MetricsView,
ResourceKind.Explore,
ResourceKind.Canvas,
ResourceKind.Theme,
ResourceKind.API,
ResourceKind.Connector,
];

export function getResourceStatus(r: V1Resource): ResourceStatus {
if (r.meta?.reconcileError) return "error";
const status = r.meta?.reconcileStatus;
Expand Down
2 changes: 2 additions & 0 deletions web-common/src/lib/i18n/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1653,6 +1653,7 @@
"status_action_describe": "Describe",
"status_action_full_refresh": "Full Refresh",
"status_action_incremental_refresh": "Incremental Refresh",
"status_action_refresh": "Refresh",
"status_action_refresh_errored_partitions": "Refresh Errored Partitions",
"status_action_view_logs": "View Logs",
"status_action_view_partitions": "View Partitions",
Expand Down Expand Up @@ -1795,6 +1796,7 @@
"status_refresh_all_confirm_title": "Refresh all sources and models?",
"status_refresh_all_sources_models": "Refresh all sources and models",
"status_refresh_errored_confirm_body": "This will re-execute all partitions that failed during their last run. The refresh will happen in the background.",
"status_refresh_description": "Refreshing this resource will update all dependent resources.",
"status_refresh_errored_confirm_title": "Refresh Errored Partitions for {modelName}?",
"status_refreshing": "Refreshing...",
"status_resource_reconciling": "Resource is currently being reconciled",
Expand Down
2 changes: 2 additions & 0 deletions web-common/src/lib/i18n/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -1662,6 +1662,7 @@
"status_action_describe": "Describir",
"status_action_full_refresh": "Actualización completa",
"status_action_incremental_refresh": "Actualización incremental",
"status_action_refresh": "Actualizar",
"status_action_refresh_errored_partitions": "Actualizar particiones con error",
"status_action_view_logs": "Ver registros",
"status_action_view_partitions": "Ver particiones",
Expand Down Expand Up @@ -1804,6 +1805,7 @@
"status_refresh_all_confirm_title": "¿Actualizar todas las fuentes y modelos?",
"status_refresh_all_sources_models": "Actualizar todas las fuentes y modelos",
"status_refresh_errored_confirm_body": "Se reejecutarán todas las particiones que fallaron en su última ejecución. La actualización se realizará en segundo plano.",
"status_refresh_description": "Actualizar este recurso actualizará todos los recursos dependientes.",
"status_refresh_errored_confirm_title": "¿Actualizar particiones con error de {modelName}?",
"status_refreshing": "Actualizando...",
"status_resource_reconciling": "El recurso se está reconciliando",
Expand Down
Loading