Skip to content

[ZEPPELIN-6631] Render the notebook repository list through a React remote behind a flag - #5472

Open
kimyenac wants to merge 6 commits into
apache:masterfrom
kimyenac:ZEPPELIN-6631
Open

[ZEPPELIN-6631] Render the notebook repository list through a React remote behind a flag#5472
kimyenac wants to merge 6 commits into
apache:masterfrom
kimyenac:ZEPPELIN-6631

Conversation

@kimyenac

Copy link
Copy Markdown
Contributor

What is this PR for?

Moves the /notebook-repos repository list and its edit surface to the React remote behind ?reactNotebookRepos, while Angular keeps the route, NotebookRepoService, the PUT, the refetch that follows it, the note-tree refresh the server broadcasts, and the fallback. Only the cards inside .content change hands; the default path stays Angular.

ZEPPELIN-6682, first commit, is the safety net this rests on: a live-server E2E proving that a successful repository save reloads notebooks and refreshes the shell note tree. It carries no React implementation and stands on its own — its spec passes with none of the port applied, so the two split cleanly if you would rather take them separately. Both are here because ZEPPELIN-6631 cannot close until that scenario passes with the React flag enabled, and ZEPPELIN-6630/6363 took the same shape.

The host follows the shape ZEPPELIN-6630 established for the configuration table rather than inventing a second one: a shouldUseReactList getter folding the flag together with a mount failure, props memoized on the only input that changes, and queryParamMap subscribed rather than read once, because navigating between /notebook-repos and /notebook-repos?reactNotebookRepos reuses the component. An onError from the remote falls back to the Angular list for the rest of the session.

ReactMountDirective now re-enters the Angular zone for every function-valued prop, not only onError as ZEPPELIN-6565 left it. The repository list is the first surface to hand the remote a real callback: its save calls back into the host, which issues the PUT and refetches. Outside the zone that work is untracked and the refresh lands late or not at all.

The remote owns no state beyond the open editor and its draft. A save hands the whole repo back with the edited values in place, not a partial patch, since the host issues the PUT and feeds the refetched list back down; the draft is rebuilt when that arrives, so a card cannot keep showing values the server already replaced. Blank settings are refused the way the Angular form's required validator refuses them, rather than sending a PUT the server would reject.

NotebookRepo and its setting type are declared in the remote because the SDK does not carry them and the shell's NotebookRepoSettingsItem is an Angular interface the remote cannot import. On visual parity: REPO_TOKENS passes fontWeightStrong through the theme provider for the same reason the configuration table does (ng-zorro draws card titles and table headers at 500, antd at 600), the card gap is pinned to 24px to match @card-padding-base, and the buttons carry the edit/save/close icons the Angular card draws through nz-icon.

What type of PR is it?

Improvement

Todos

None

What is the Jira issue?

How should this be tested?

  • npm run test:shell: 51 tests across 11 files. react-mount.directive.spec.ts gains the zone case for a non-onError callback.
  • projects/zeppelin-react vitest: 76 tests across 12 files, including the 10 in NotebookRepoList.spec.tsx.
  • Production builds: npm run build:react (NotebookRepoList present in remoteEntry.js) and npm run build:projects.
  • Manually against a live server in both branches of the flag, anonymous mode:
    • The save round trip issues PUT /api/notebook-repositories followed by the host's refetch, and the card returns to read mode showing the new value.
    • A blank setting disables Save; Cancel restores the previous value and re-entering edit shows it.
    • Reading the repository names and setting rows from each branch returns byte-identical output.
    • The note-tree scenario ZEPPELIN-6682 covers, with the React flag on: a note created over REST is absent from a second client's home note tree, stays absent while nothing is saved, and appears after the repository save. Skipping the save leaves it absent, which is the control that makes the assertion mean something.
    • Aborting remoteEntry.js falls back to the Angular list, with no mount host in the DOM.
  • Playwright was not run locally; the two new specs and the ZEPPELIN-6682 spec's two flag legs run for the first time in run-playwright-e2e-tests.
  • DROPDOWN settings are covered by unit tests only — the server used for the manual pass exposes one repository with a single INPUT setting.

Screenshots (if appropriate)

The two lists are hard to tell apart, which is the intent. Header weight, card gap and button icons match; the remaining difference is antd's own row height.

Questions:

  • Does the license files need to update? No
  • Is there breaking changes for older versions? No, the flag defaults to off and the Angular list is unchanged when it is
  • Does this needs documentation? The two places that enumerate the flagged React surfaces are updated in the last commit

The resolved /notebook-repos set covers the form, its validation and the
save round trip, but stops at the repository list. It never reaches what
the server does next: NotebookRepoRestApi broadcasts a reloaded note list
after a successful update, and the shell note tree is what consumes it.

The spec drives two clients. The header's note tree is destroyed when the
dropdown closes and calls listNodes() again on every open, so it cannot
tell a broadcast from its own refetch. The home route keeps a tree
mounted instead, which leaves the broadcast as the only thing that can
change it, so one client watches the tree while the other saves.

A note is created over REST between the two assertions. Creating one does
not broadcast the list, so the tree has no way to know about it until the
save arrives; without that step a tree that refreshed on its own would
read as success. Dropping the updateRepo call fails the spec.

The save leaves the settings as they are, the way the existing workflow
spec does, so the repository configuration is unchanged. The note is
removed again in a finally block rather than left to the folder cleanup,
since it is created at the root to stay visible in a collapsed tree.

The repository card carries a data-testid and the page models select on
it rather than on zeppelin-notebook-repo-item and nz-table. This page is
a migration seam, and a model pinned to the Angular element would take
the whole set down the moment the React list takes over.
ZEPPELIN-6565 wrapped onError so a remote-invoked error handler runs
back inside NgZone. It stopped there, and every other function arriving
through reactProps is still handed to the remote untouched.

That was fine while onError was the only callback any surface passed.
The notebook repository list is the first to hand the remote a real one:
its save calls back into the host, which then issues the PUT and refetches
the list. Outside the zone that work is untracked, so the refresh lands
late or not at all, which is the failure ZEPPELIN-6631 asks to check for
before building on top of it.

withHostCallbacks now wraps every function-valued prop. A callback that
throws is logged rather than rethrown, since the caller is React and an
exception would surface as a render error in a tree the host does not
own. Non-function props keep their identity so the remote can still
memoize on them.
…emote behind a flag

The list and its edit surface move to the React remote behind
?reactNotebookRepos, while Angular keeps the route, NotebookRepoService,
the PUT, the refetch that follows it, and the note-tree refresh the server
broadcasts. Only the repository cards inside .content change hands.

The host follows the shape ZEPPELIN-6630 established for the configuration
table rather than inventing a second one: a shouldUseReactList getter that
folds the flag together with a mount failure, props memoized on the only
input that changes, and queryParamMap subscribed rather than read once,
because navigating between /notebook-repos and /notebook-repos?
reactNotebookRepos reuses the component. An onError from the remote falls
back to the Angular list for the rest of the session.

The remote owns no state beyond the open editor and its draft. A save hands
the whole repo back to the host with the edited values in place, not a
partial patch, since the host is what issues the PUT and then feeds the
refetched list back down. The draft is rebuilt when that new list arrives,
so a card cannot keep showing values the server has already replaced.

Blank settings are refused in the remote the way the Angular form's required
validator refuses them, rather than sending a PUT the server would reject.

NotebookRepo and its setting type are declared in the remote because the SDK
does not carry them; the shell's own NotebookRepoSettingsItem is an Angular
interface the remote cannot import. REPO_TOKENS passes fontWeightStrong
through the theme provider for the same reason the configuration table does:
ng-zorro draws card titles and table headers at 500 where antd uses 600.
…wo branches

Two specs, for the two things a flagged surface has to prove.

react-notebook-repo-list.spec.ts covers which branch is live and that they
agree. Both branches render the notebook-repo-item id, so the question is
about the mount host around it, not the card. The parity test reads the
repository names and setting rows from each branch and compares them: the
host still owns the fetch and the sort, so a remote that reshaped what it
was handed would show up here. The fallback test aborts remoteEntry.js and
awaits the request before asserting, because Angular is the default branch
and the assertions would otherwise pass on a flag that never took.

notebook-repos-save-reloads-note-tree.spec.ts, from ZEPPELIN-6682, now runs
on both branches. This is what ZEPPELIN-6631 asks for before it can close:
the reload is the host's job either way, so a React list that swallowed the
save would surface as a note tree that never picks the note up. The body is
unchanged apart from reading the repository name from data-repo-name, which
both branches carry, rather than from the ng-zorro card title.
…faces

Both places that enumerate the flagged surfaces stopped at the configuration
table. e2e/AGENTS.md also counted them, so the count moves with the list.
Review follow-ups on the React list, all parity rather than behaviour.

The card gap was hardcoded at 16px where the Angular card spaces itself with
@card-padding-base, 24px in the default theme, so the two lists did not line
up side by side. The buttons were missing the edit, save and close icons the
Angular card draws through nz-icon.

Two comments that restated their own code are shorter: the withHostCallbacks
docblock and the prefer-web-first-assertions justification.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant