Skip to content

Fixes 30704: Gate agent wizard Next on form readiness and clear the stuck dashboard service loader - #30705

Open
aniketkatkar97 wants to merge 3 commits into
mainfrom
fix-serviceingestion-nightly-failures
Open

Fixes 30704: Gate agent wizard Next on form readiness and clear the stuck dashboard service loader#30705
aniketkatkar97 wants to merge 3 commits into
mainfrom
fix-serviceingestion-nightly-failures

Conversation

@aniketkatkar97

@aniketkatkar97 aniketkatkar97 commented Jul 30, 2026

Copy link
Copy Markdown
Member

Describe your changes:

Fixes #30704

ServiceIngestion.spec.ts was failing on the nightly AUT run. I pulled the retained traces (16 zips) and found four distinct failure signatures, not one flake — two genuine product bugs plus two stale selectors. Because the affected blocks are test.describe.serial, one root cause takes out a whole service suite (e.g. Superset never reaches Update schedule options because Update description… dies first).

Failure Suites Root cause
getByTestId('schedular-schedule') times out (60 s) BigQuery, MySQL, Redshift Wizard next-button silently no-ops while the RJSF form is suspended
expect(loader).toHaveCount(0) stuck at 1 (30 s) Metabase, Superset fetchDashboardsDataModel never clears isServiceLoading
#root/dbtConfigSource__oneof_select times out Redshift RJSF native oneOf select replaced by a react-aria Select
Test Connection Done times out (180 s) Api Service CollateSaaS runner never completes the Rest workflow — infra, out of scope

1. next-button is a dead click while the Configure Ingestion form lazy-loads. AddIngestionPage / EditIngestionPage render the footer's next-button with no disabled state; pressing it calls addIngestionRef.current?.submit()workflowFormRef.current?.submit()formRef.current?.submit(). IngestionWorkflowForm wraps its RJSF <Form> in <Suspense> and passes bare React.lazy templates, so until those chunks resolve formRef.current is null and submit() does nothing — no toast, no validation error, the wizard just stays on step 1. The trace is unambiguous: edit-button at t=391905, next-button at t=392080 (175 ms later), wizard data fetches landing at t=392102 — after the click. Regressed in #28569, which moved the advance button out of the form (it used to be a native htmlType="submit", which could not race a ref).

IngestionWorkflowForm now reports readiness from a small FormReadyNotifier rendered inside the Suspense boundary (so its effect can only fire once every lazy template has resolved), AddIngestion forwards that up as onStepReadyChange, and both host pages disable next-button until the active step is ready — mirroring the footerNextDisabled pattern EmbeddedAddServicePage already uses. Step 2 (ScheduleIntervalStep) is a static import, so it reports ready immediately.

While in there: {!hideFooter && (…)} evaluates to false, and RJSF treats falsy children as "no children" (children ? children : <SubmitButton/>), so it was injecting its own stray "Submit" button into the wizard next to the real footer. Suppressed via ui:submitButtonOptions: { norender: true }.

2. Dashboard services leave a table spinner running forever. ServiceDetailsPage.fetchDashboardsDataModel set isServiceLoading(true) with no finally, unlike its sibling getOtherDetails. Its effect re-runs on activeTab, and getOtherDetails early-returns on a non-entity tab, so switching Dashboards → Agents left the flag stuck on. antd Tabs keeps the visited pane mounted, so the Dashboards <Table loading> spinner stayed DOM-attached for the rest of the page's life. Invisible to the user, but fatal to any page-wide [data-testid="loader"] assertion — and dashboard-service-specific, which is exactly why only Superset and Metabase failed.

3. Playwright fixes. New waitForIngestionWorkflowForm helper (mirrors the existing waitForServiceConnectionForm) used at every step-1 advance; openAgentScheduleStep extracted from the four duplicated blocks in updateScheduleOptions; the agent-tab loader waits in updateDescriptionForIngestedTables scoped the way addIngestionPipeline already does it; the dbt config source moved onto the existing selectOneOfOption helper; and the two [data-testid="submit-btn"] clicks replaced with next-button — that testid does not exist in this wizard (verified against @rjsf/core 5.24.13 Form.render; the button rendered there is RJSF's default and carries no testid).

selectOneOfOption itself had a latent bug found while dry-running: its Select branch preferred clicking react-aria's visually hidden native combobox (with force: true), which never opens the listbox. Only the tabs branch was exercised by passing tests. It now clicks the wrapper — the pattern ServiceBaseClass.createService already uses successfully for the ingestion-runner select — and scopes options to the visible popover.

Type of change:

  • Bug fix

High-level design:

The wizard footer lives in the host page while the form it submits lives three levels down behind a Suspense boundary, so "is this step submittable" has to travel upwards. Rather than reach into the ref, readiness is reported as a plain callback chain:

IngestionWorkflowForm  --onReady-->  AddIngestion  --onStepReadyChange-->  Add/EditIngestionPage
   (FormReadyNotifier                  (combines with                        (isDisabled={!isStepReady})
    inside <Suspense>)                  activeIngestionStep)

FormReadyNotifier is a sibling of <Form> rather than one of its children on purpose — RJSF's children slot doubles as the submit-button override, and putting anything there changes button rendering. Being inside the boundary is what makes the signal correct: any suspending template defers the whole subtree, so the effect cannot fire early.

Alternatives rejected:

  • Queue the submit and flush it when the form mounts. Hides the problem instead of fixing it, and a click that appears to do nothing for 200 ms then acts is worse UX than a briefly disabled button.
  • Test-side wait only. Leaves a real dead button in the product for anyone clicking fast.
  • Drop the lazy templates. Would undo deliberate code-splitting (Improve connector setup UI flow #28569, d82b7cf0cc).

No schema, API, or migration impact. The new props are optional, so the third AddIngestion-shaped consumer (EmbeddedAddServicePage, which already has its own gate) is unaffected.

Tests:

Use cases covered

  • Opening Edit Metadata Agent and pressing Next as soon as it is enabled advances to the Schedule Interval step (previously a silent no-op).
  • The wizard's Next button is disabled while the Configure Ingestion form is still loading, and enabled once it mounts.
  • Switching from a dashboard service's Dashboards tab to Agents no longer leaves the entity table in a loading state — including when the data model count request fails.
  • Adding a dbt agent to a Redshift service selects DBT S3 Config through the react-aria oneOf select.

Unit tests

  • I added unit tests for the new/changed logic.
  • Files added/updated:
    • src/components/Settings/Services/AddIngestion/AddIngestion.test.tsx — the configure step reports ready only after the workflow form mounts; the schedule step reports ready immediately.
    • src/pages/AddIngestionPage/AddIngestionPage.test.tsxnext-button stays disabled until the active step reports ready.
    • src/pages/ServiceDetailsPage/ServiceDetailsPage.test.tsx — the entity tab is not left loading after moving off it, on both the resolved and rejected data-model-count paths.
  • yarn test src/components/Settings/Services src/pages/AddIngestionPage src/pages/ServiceDetailsPage28 suites, 322 tests passing.
  • The ServiceDetailsPage test was confirmed to actually fail without the fix (data-service-loading="true") before being kept.

Backend integration tests

  • Not applicable (no backend API changes).

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • I added Playwright E2E tests for UI changes.
  • Files added/updated:
    • playwright/e2e/nightly/ServiceIngestion.spec.ts — new Edit agent wizard step navigation describe: creates a MySQL service + deployed metadata pipeline via API, opens the edit wizard, and asserts Next reaches the schedule step. No ingestion runtime needed, so it is a cheap regression guard for the 60 s timeout.
    • playwright/utils/serviceIngestion.ts, playwright/utils/serviceFormUtils.ts, playwright/support/entity/ingestion/{ServiceBaseClass,MySqlIngestionClass,PostgresIngestionClass,RedshiftWithDBTIngestionClass}.ts — helper and selector fixes described above.

Manual testing performed

The Playwright changes target the nightly AUT environment (Redshift / Superset / BigQuery credentials), which I do not have locally, so the E2E suite has not been executed on this branch — it needs a nightly run to confirm. What was verified locally:

  1. yarn test … — 28 suites / 322 tests green, and the loader test verified RED without the finally.
  2. npx tsc --noEmit — no new errors on the changed files (the one pre-existing AJV validator variance error in IngestionWorkflowForm.tsx reproduces on a stashed tree).
  3. make ui-checkstyle-changed — exit 0, no reformat diff.
  4. Traced every other caller that clicks the agent wizard's next-button (playwright/utils/autoClassification.ts, playwright/utils/profilerForm.ts, playwright/e2e/Features/StorageMetadataAgentForm.spec.ts) to confirm each already waits on the form before advancing, so the new disabled state cannot hang them.

UI screen recording / screenshots:

No visual change — the only user-visible difference is that the wizard's Next button is briefly disabled instead of dead while the form loads, and a stray RJSF "Submit" button no longer appears next to the footer. Trace evidence for the original failure, from the nightly run:

t=391905  click [data-testid="edit-button"]
t=392080  click [data-testid="next-button"]      <-- 175 ms later
t=392102  GET /services/databaseServices/name/…  <-- wizard data arrives AFTER the click
t=392316  waiting for getByTestId('schedular-schedule') … 60 s timeout

Checklist:

  • I have read the CONTRIBUTING document.

  • My PR title is Fixes <issue-number>: <short explanation>

  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.

  • I have commented on my code, particularly in hard-to-understand areas.

  • For JSON Schema changes: I updated the migration scripts or explained why it is not needed. (No schema changes.)

  • For UI changes: I attached a screen recording and/or screenshots above. (Explained above — no visual change.)

  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

  • I have added a test that covers the exact scenario we are fixing. For complex issues, comment the issue number in the test for future reference.

🤖 Generated with Claude Code

Greptile Summary

This PR improves ingestion-wizard synchronization and separates dashboard count loading from entity-table loading.

  • Gates Add and Edit wizard navigation until the active form reports that it is mounted.
  • Removes dashboard data-model count requests from the shared entity loading and paging state.
  • Updates unit and Playwright coverage and synchronizes selectors with the current form controls.

Confidence Score: 4/5

The PR is not yet safe to merge because returning to the configure step can still expose an enabled Next button before the remounted form is ready.

The readiness state is only changed from false to true and survives the conditional unmount of the workflow form, so navigating from configure to schedule and back can reuse a stale ready signal; clicking Next during the new Suspense interval follows optional-chained null refs and silently performs no submission. The dashboard loader issue is resolved because the count request no longer owns the shared entity loading or paging state.

Files Needing Attention: openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/AddIngestion.component.tsx

Important Files Changed

Filename Overview
openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/AddIngestion/AddIngestion.component.tsx Propagates form readiness to host pages, but retains readiness across the workflow form's step-driven remount.
openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Ingestion/IngestionWorkflowForm/IngestionWorkflowForm.tsx Signals readiness from inside the Suspense boundary and suppresses RJSF's fallback submit control.
openmetadata-ui/src/main/resources/ui/src/pages/AddIngestionPage/AddIngestionPage.component.tsx Disables the external Next button until the active ingestion step reports readiness.
openmetadata-ui/src/main/resources/ui/src/pages/EditIngestionPage/EditIngestionPage.component.tsx Applies the same readiness gate to the edit-ingestion wizard.
openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx Separates dashboard data-model count state from entity-table loading and paging state.
openmetadata-ui/src/main/resources/ui/playwright/utils/serviceFormUtils.ts Opens react-aria one-of selectors through their visible wrapper and scopes option selection to the visible popover.
openmetadata-ui/src/main/resources/ui/playwright/utils/serviceIngestion.ts Adds a reusable wait that synchronizes tests with workflow-form readiness.

Reviews (3): Last reviewed commit: "fix(ui): stop the data model count fetch..." | Re-trigger Greptile

…e loader

Fixes #30704

The Add/Edit agent wizard's footer `next-button` had no disabled state, so
pressing it before the lazily loaded RJSF templates resolved called
`submit()` against a null form ref and silently did nothing. Report step
readiness from inside the Suspense boundary and disable the button until the
form is mounted. Also suppress the stray RJSF default submit button that
appeared because a falsy `children` expression reads as "no children".

`fetchDashboardsDataModel` never cleared `isServiceLoading`, so switching
tabs on a dashboard service left the still-mounted entity tab's table
spinner running for the rest of the page's life.

On the Playwright side, add `waitForIngestionWorkflowForm` and use it at
every step-1 advance, scope the agent-tab loader waits the way
`addIngestionPipeline` already does, move the dbt config source onto the
react-aria `oneOf` select, and replace the two `submit-btn` clicks that no
longer resolve. `selectOneOfOption` now opens the select by clicking its
wrapper instead of react-aria's visually hidden native combobox, which
never opened the listbox.

Co-Authored-By: Claude <noreply@anthropic.com>
@aniketkatkar97
aniketkatkar97 requested a review from a team as a code owner July 30, 2026 12:58
Copilot AI review requested due to automatic review settings July 30, 2026 12:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Jul 30, 2026
@aniketkatkar97 aniketkatkar97 moved this to In Review / QA 👀 in Shipping Jul 30, 2026
Copilot AI review requested due to automatic review settings July 30, 2026 13:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

`fetchDashboardsDataModel` only feeds the Data Model tab's count label, but
it was also driving `isServiceLoading` and, on error, the entity list paging
— both of which belong to `getOtherDetails`. Sharing the flag meant whichever
request settled first decided the entity table's spinner, so a fast count
query could clear it while the entity list was still in flight.

Dropping the writes fixes the stuck spinner without introducing that race,
and is simpler than clearing the flag in a `finally`.

Co-Authored-By: Claude <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 30, 2026 13:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gitar-bot

gitar-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown
Code Review ✅ Approved

Gates the agent wizard Next button on lazy-loaded form readiness and ensures dashboard service loading states clear correctly when switching tabs. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar | Powered by Gitar — free for open source

@github-actions

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 65%
65.8% (77012/117036) 49.66% (46287/93206) 50.89% (13939/27386)

@sonarqubecloud

Copy link
Copy Markdown

@aniketkatkar97
aniketkatkar97 added this pull request to the merge queue Jul 30, 2026
@aniketkatkar97 aniketkatkar97 added the To release Will cherry-pick this PR into the release branch label Jul 30, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs To release Will cherry-pick this PR into the release branch UI UI specific issues

Projects

Status: In Review / QA 👀

Development

Successfully merging this pull request may close these issues.

ServiceIngestion nightly AUT failures: agent wizard Next is a no-op while the form lazy-loads, and dashboard services leave a table loader spinning

3 participants