This is an automated email from the ASF dual-hosted git repository.
github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/texera.git
The following commit(s) were added to refs/heads/main by this push:
new 5264df2861 fix(frontend): say the warehouse is missing on the form
view's run button (#8592)
5264df2861 is described below
commit 5264df2861840bf8b1662ff2e1814b8aa80215db
Author: Meng Wang <[email protected]>
AuthorDate: Fri Sep 18 19:39:26 2026 +0000
fix(frontend): say the warehouse is missing on the form view's run button
(#8592)
### What changes were proposed in this PR?
The form view's run button enumerates every state a run cannot start
from — invalid workflow, empty workflow, connecting, no computing unit,
a read-only unit — so that, in its own words, "the reader is never sent
to press a button that does nothing". The warehouse requirement added in
#8551 was missing from that list: with the feature enabled and nothing
picked, the button read `Run` and looked ready, and the click was
refused deeper down in `ExecuteWorkflowService`, with a toast.
It now names what is missing, ordered after the computing unit as the
canvas orders them, and stays disabled — the warehouse is picked in the
embedded selector, the same reasoning the no-unit case already gives.
The condition it reads is the one `ExecuteWorkflowService` refuses on —
the boot-time flag and the picked warehouse, both root-scoped — so the
button predicts that refusal exactly.
Flag off (the default): `warehouseRequiredButMissing` is never true, so
the button behaves exactly as before.
### Any related issues, documentation, discussions?
Closes #8591. Follows #8551. Part of #6870.
### How was this PR tested?
- Two Vitest cases added: the button names the missing warehouse and
stays disabled, and returns to `Run` once one is picked. The workspace
suite passes in full: 3324 tests.
- Failure path verified rather than assumed: the new case was removed on
purpose and the suite confirmed to fail for the expected reason before
being restored.
### Was this PR authored or co-authored using generative AI tooling?
Generated-by: Claude Code (claude-opus-5, claude-fable-5)
---
.../workflow-form/workflow-form.component.spec.ts | 31 +++++++++++++++++++++-
.../workflow-form/workflow-form.component.ts | 15 ++++++++++-
.../workflow-form/workflow-form.spec-harness.ts | 10 ++++++-
3 files changed, 53 insertions(+), 3 deletions(-)
diff --git
a/frontend/src/app/workspace/component/workflow-form/workflow-form.component.spec.ts
b/frontend/src/app/workspace/component/workflow-form/workflow-form.component.spec.ts
index 50edda7f4b..0c3e5e1f8a 100644
---
a/frontend/src/app/workspace/component/workflow-form/workflow-form.component.spec.ts
+++
b/frontend/src/app/workspace/component/workflow-form/workflow-form.component.spec.ts
@@ -71,7 +71,8 @@ describe("WorkflowFormComponent", () => {
h.datePipe as any,
h.panelResizeService as any,
h.validationWorkflowService as any,
- h.config as any
+ h.config as any,
+ h.warehouseService as any
);
return component;
};
@@ -1506,6 +1507,34 @@ describe("WorkflowFormComponent", () => {
expect(component.runButtonState).toEqual({ label: "Computing Unit",
icon: "plus-circle", disabled: true });
});
+ it("names the missing warehouse instead of offering a run that would be
refused (#8591)", () => {
+ build(formViewWorkflow).ngOnInit();
+ makeReady();
+ h.config.env.warehouseEnabled = true;
+ h.warehouseService.selectWarehouse(undefined);
+
+ expect(component.runButtonState).toEqual({ label: "Warehouse", icon:
"plus-circle", disabled: true });
+ });
+
+ it("says 'No access' before the warehouse, since picking one would not
unblock a reader", () => {
+ build(formViewWorkflow).ngOnInit();
+ makeReady();
+ (component as any).selectedUnit = { accessPrivilege: "READ" };
+ h.config.env.warehouseEnabled = true;
+ h.warehouseService.selectWarehouse(undefined);
+
+ expect(component.runButtonState).toEqual({ label: "No access", icon:
"lock", disabled: true });
+ });
+
+ it("runs once a warehouse is picked", () => {
+ build(formViewWorkflow).ngOnInit();
+ makeReady();
+ h.config.env.warehouseEnabled = true;
+ h.warehouseService.selectWarehouse(7);
+
+ expect(component.runButtonState).toEqual({ label: "Run", icon:
"caret-right", disabled: false });
+ });
+
it("offers Run once a unit is up and the graph is valid", () => {
build(formViewWorkflow).ngOnInit();
makeReady();
diff --git
a/frontend/src/app/workspace/component/workflow-form/workflow-form.component.ts
b/frontend/src/app/workspace/component/workflow-form/workflow-form.component.ts
index 2b9433c482..0c3c182b12 100644
---
a/frontend/src/app/workspace/component/workflow-form/workflow-form.component.ts
+++
b/frontend/src/app/workspace/component/workflow-form/workflow-form.component.ts
@@ -53,6 +53,7 @@ import { FormBindingService, ResolvedField } from
"../../service/form-binding/fo
import { WorkflowActionService } from
"../../service/workflow-graph/model/workflow-action.service";
import { ValidationWorkflowService } from
"../../service/validation/validation-workflow.service";
import { GuiConfigService } from "../../../common/service/gui-config.service";
+import { WarehouseService } from
"../../../common/service/warehouse/warehouse.service";
import { WorkflowConsoleService } from
"../../service/workflow-console/workflow-console.service";
import { WorkflowResultService } from
"../../service/workflow-result/workflow-result.service";
import { PanelResizeService } from
"../../service/workflow-result/panel-resize/panel-resize.service";
@@ -299,7 +300,8 @@ export class WorkflowFormComponent implements OnInit,
OnDestroy {
// Same source the operator canvas reads its "Invalid" / "Empty" states
from, so Run is
// disabled here exactly when it is disabled there.
private validationWorkflowService: ValidationWorkflowService,
- private config: GuiConfigService
+ private config: GuiConfigService,
+ private warehouseService: WarehouseService
) {}
ngOnInit(): void {
@@ -1526,6 +1528,12 @@ export class WorkflowFormComponent implements OnInit,
OnDestroy {
);
}
+ /** The exact condition ExecuteWorkflowService refuses a run on, so the
button can say it first
+ * instead of starting nothing and explaining in a toast. */
+ public get hasNoWarehouse(): boolean {
+ return this.config.env.warehouseEnabled &&
this.warehouseService.getSelectedWarehouseIdValue() === undefined;
+ }
+
/** No unit chosen yet: the button names what is missing and stays disabled,
because the unit is
* picked in the embedded selector -- unlike the canvas, where that button
is itself the click
* target for creating one. */
@@ -1573,6 +1581,11 @@ export class WorkflowFormComponent implements OnInit,
OnDestroy {
if (!this.hasUnitWriteAccess) {
return { label: "No access", icon: "lock", disabled: true };
}
+ // Last of the blocked states: picking a warehouse is the only one of them
the reader can act
+ // on here, so naming it earlier would send a reader without write access
to fix the wrong thing.
+ if (this.hasNoWarehouse) {
+ return { label: "Warehouse", icon: "plus-circle", disabled: true };
+ }
return { label: "Run", icon: "caret-right", disabled: false };
}
diff --git
a/frontend/src/app/workspace/component/workflow-form/workflow-form.spec-harness.ts
b/frontend/src/app/workspace/component/workflow-form/workflow-form.spec-harness.ts
index dc5cb7609c..9ca8be1001 100644
---
a/frontend/src/app/workspace/component/workflow-form/workflow-form.spec-harness.ts
+++
b/frontend/src/app/workspace/component/workflow-form/workflow-form.spec-harness.ts
@@ -308,7 +308,14 @@ export function setupHarness() {
// default so a rebuild is never suppressed, and overridden by the tests
that probe typing.
const host = { nativeElement: { querySelector: () => null, contains: () =>
false } };
const datePipe = { transform: () => "01/01/2026 00:00:00" };
- const config = { env: { formViewEnabled: true } };
+ const config = { env: { formViewEnabled: true, warehouseEnabled: false } };
+ // The run button asks the same pair ExecuteWorkflowService refuses on: the
flag above and the
+ // pick below.
+ let selectedWarehouseId: number | undefined = undefined;
+ const warehouseService = {
+ getSelectedWarehouseIdValue: () => selectedWarehouseId,
+ selectWarehouse: (whid: number | undefined) => (selectedWarehouseId =
whid),
+ };
// Point the persist mock at `workflow`; each spec supplies the remaining
constructor
// arguments in its own order via the named mocks above.
@@ -343,6 +350,7 @@ export function setupHarness() {
host,
datePipe,
config,
+ warehouseService,
workflowChangedStream,
formBindingChanged,
workflowMetaDataChangedStream,