Skip to content

fix(FR-2979): display sender on shared folder invitations in summary page#7607

Merged
graphite-app[bot] merged 1 commit into
mainfrom
fix/FR-2979-shared-folder-invite-sender
May 29, 2026
Merged

fix(FR-2979): display sender on shared folder invitations in summary page#7607
graphite-app[bot] merged 1 commit into
mainfrom
fix/FR-2979-shared-folder-invite-sender

Conversation

@agatha197

@agatha197 agatha197 commented May 27, 2026

Copy link
Copy Markdown
Contributor

Linked Jira: FR-2979
Backend counterpart: BA-6193 (merged, SSO empty-email)
Backend follow-up: BA-6228 (expose inviter_user_email as a separate DTO field)

TL;DR

FR-2979 ("Summary page invitation card shows empty From:") has two independent root causes. This PR fixes the frontend one. The other one is server-side and lives in BA-6193. Both need to land for FR-2979 to be fully resolved end-to-end.

# Root cause Where it lives Status
1 Both the Summary card and FolderInvitationResponseModal read invitation.inviter blindly. The legacy backend used to put the inviter email there, but manager v25.6.0 plans to expose it under a separate inviter_user_email field. This PR gates the read with the existing invitation-inviter-email capability flag and falls back to the legacy inviter field for older managers. Frontend (this PR) Fixed here
2 Backend persists User.email = "" for SSO-created accounts and mirrors it straight into the invitation response, so the sender renders blank regardless of the field name; the accept handler's if not inviter_email check also raises on the empty string. Backend (BA-6193) Tracked separately

After this PR alone: users invited by a manually-created sender on a manager that exposes inviter_user_email see the sender's email again. Users whose inviter is an SSO-created account still see blank — unblocked only by BA-6193.

What this PR does

Use the same capability-check pattern that FolderInvitationResponseModal.tsx originally followed (added in FR-1101): read baiClient.supports('invitation-inviter-email') and choose the field accordingly. Both call sites also fall back to '-' to avoid rendering From: undefined when neither field has a value.

  • react/src/components/FolderInvitationResponseModal.tsx — add useSuspendedBackendaiClient + hasInviterEmail, render (hasInviterEmail ? item.inviter_user_email : item.inviter) || '-' in the "From" descriptions item.
  • react/src/components/SummaryPageItems/SummaryItemInvitation.tsx — same gating in the card title.

Known limitation (and the backend follow-up that unblocks it)

The current VFolderInvitationDTO (src/ai/backend/common/dto/manager/vfolder/response.py in lablup/backend.ai) only declares the single inviter field:

class VFolderInvitationDTO(BackendAISchema):
    inviter: str = Field(description="Inviter email, or username if email is empty")
    ...

The handler collapses both possibilities into that field (inviter=info.inviter_user_email or info.inviter_username or ""). So on managers where the capability flag is true but the DTO has not yet been expanded with inviter_user_email, this PR will render From: - for every invitation. Older managers (capability = false) continue to read the legacy inviter field and work as before.

The backend side needs to add inviter_user_email as a separate DTO field for the capability-gated branch to actually show the email. Tracked separately in BA-6228 — until that lands, manager ≥ 25.6.0 will render From: - for every invitation. Older managers (capability = false) continue to read the legacy inviter field and work as before.

What this PR explicitly does NOT fix — and why the frontend can't

The SSO-side problem above is not addressable from the client:

  • The invitation payload contains the empty email itself; there is no other identifying field to fall back to.
  • The payload has no is_sso / external_id flag, so the frontend cannot even differentiate SSO rows to apply a special UI.
  • A cosmetic "Unknown sender" fallback would only mask the symptom while leaving the accept-flow exception in BA-6193 untouched.

So the fix has to land where the empty email originates — the backend. See BA-6193.

Verification

  • bash scripts/verify.sh — Relay, Lint, Format PASS. TypeScript surfaces only pre-existing failures in unrelated files; no new errors in the touched files.
  • Manual: invite a manually-created user from another manually-created user on a manager that exposes inviter_user_email; the Summary card and the invitation modal both show the inviter's email. On a manager that only returns inviter (current main), both fall back to the legacy field and render correctly. SSO-related rows remain blank, expected until BA-6193 lands.

Checklist

  • Documentation — n/a (capability-check pattern already documented by FR-1101's PR)
  • Minimum required manager version — no change (the existing capability flag covers both old and new managers)
  • Minimum requirements to check during review — log in as the invitee and confirm the Summary invitation card title and the FolderInvitationResponseModal "From:" field both show the inviter's email (or - if not available on the current manager)
  • Test case(s) — covered by the e2e in e2e(FR-2982): assert inviter email is shown in folder invitation modal #7610 (FR-2982)

agatha197 commented May 27, 2026

Copy link
Copy Markdown
Contributor Author

How to use the Graphite Merge Queue

Add either label to this PR to merge it via the merge queue:

  • flow:merge-queue - adds this PR to the back of the merge queue
  • flow:hotfix - for urgent changes, fast-track this PR to the front of the merge queue

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has required the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for react-coverage (./react)

Status Category Percentage Covered / Total
🔵 Lines 6.53% 1800 / 27544
🔵 Statements 5.35% 1995 / 37256
🔵 Functions 5.28% 297 / 5621
🔵 Branches 3.73% 1300 / 34842
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
react/src/components/FolderInvitationResponseModal.tsx 0% 0% 0% 0% 27-142
react/src/components/SummaryPageItems/SummaryItemInvitation.tsx 0% 0% 0% 0% 18-81
Generated in workflow #1194 for commit 17c6056 by the Vitest Coverage Report Action

@agatha197 agatha197 force-pushed the fix/FR-2979-shared-folder-invite-sender branch from fa7cde6 to 26a8be8 Compare May 27, 2026 05:05
@agatha197 agatha197 marked this pull request as ready for review May 27, 2026 05:05
Copilot AI review requested due to automatic review settings May 27, 2026 05:05
@agatha197 agatha197 marked this pull request as draft May 27, 2026 05:06

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.

Pull request overview

Fixes the Summary page invitation card showing an empty sender by applying the existing invitation-inviter-email capability check (already used in FolderInvitationResponseModal) so inviter_user_email is read on manager ≥25.6.0 while falling back to inviter on older managers.

Changes:

  • Add baiClient.supports('invitation-inviter-email') check in SummaryItemInvitation.
  • Render inviter_user_email when supported, fall back to inviter otherwise.
  • Add 'use memo' directive to the component.

@agatha197 agatha197 marked this pull request as ready for review May 27, 2026 05:34
@agatha197 agatha197 requested review from nowgnuesLee and yomybaby May 27, 2026 09:57
@agatha197 agatha197 force-pushed the fix/FR-2979-shared-folder-invite-sender branch from 26a8be8 to 147f8b1 Compare May 27, 2026 10:02
@agatha197 agatha197 force-pushed the fix/FR-2979-shared-folder-invite-sender branch from 147f8b1 to 25baf00 Compare May 28, 2026 02:54
@github-actions github-actions Bot added size:S 10~30 LoC and removed size:XS ~10 LoC labels May 28, 2026
@agatha197 agatha197 marked this pull request as draft May 28, 2026 05:38
@agatha197 agatha197 force-pushed the fix/FR-2979-shared-folder-invite-sender branch from 25baf00 to f849b3a Compare May 28, 2026 05:54
@agatha197 agatha197 marked this pull request as ready for review May 28, 2026 05:55
Comment thread react/src/components/SummaryPageItems/SummaryItemInvitation.tsx Outdated
@agatha197 agatha197 force-pushed the fix/FR-2979-shared-folder-invite-sender branch from f849b3a to 005aaf8 Compare May 28, 2026 07:20
@agatha197 agatha197 requested a review from nowgnuesLee May 28, 2026 07:21
@agatha197 agatha197 force-pushed the fix/FR-2979-shared-folder-invite-sender branch from 8515841 to 868c4a8 Compare May 28, 2026 23:01

@yomybaby yomybaby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@yomybaby yomybaby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@yomybaby yomybaby left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@nowgnuesLee nowgnuesLee 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.

LGTM

@graphite-app

graphite-app Bot commented May 29, 2026

Copy link
Copy Markdown

Merge activity

…page (#7607)

Linked Jira: [FR-2979](https://lablup.atlassian.net/browse/FR-2979)
Backend counterpart: [BA-6193](https://lablup.atlassian.net/browse/BA-6193) (merged, SSO empty-email)
Backend follow-up: [BA-6228](https://lablup.atlassian.net/browse/BA-6228) (expose `inviter_user_email` as a separate DTO field)

<!-- `Resolves #N (FR-2979)` will be added once the Jira→GitHub clone webhook fires. -->

## TL;DR

FR-2979 ("Summary page invitation card shows empty `From:`") has **two independent root causes**. This PR fixes the frontend one. The other one is server-side and lives in BA-6193. Both need to land for FR-2979 to be fully resolved end-to-end.

| # | Root cause | Where it lives | Status |
| --- | --- | --- | --- |
| 1 | Both the Summary card and `FolderInvitationResponseModal` read `invitation.inviter` blindly. The legacy backend used to put the inviter email there, but manager v25.6.0 plans to expose it under a separate `inviter_user_email` field. This PR gates the read with the existing `invitation-inviter-email` capability flag and falls back to the legacy `inviter` field for older managers. | Frontend (this PR) | Fixed here |
| 2 | Backend persists `User.email = ""` for SSO-created accounts and mirrors it straight into the invitation response, so the sender renders blank regardless of the field name; the accept handler's `if not inviter_email` check also raises on the empty string. | Backend ([BA-6193](https://lablup.atlassian.net/browse/BA-6193)) | Tracked separately |

After **this PR alone**: users invited by a manually-created sender on a manager that exposes `inviter_user_email` see the sender's email again. Users whose inviter is an SSO-created account still see blank — unblocked only by BA-6193.

## What this PR does

Use the same capability-check pattern that `FolderInvitationResponseModal.tsx` originally followed (added in FR-1101): read `baiClient.supports('invitation-inviter-email')` and choose the field accordingly. Both call sites also fall back to `'-'` to avoid rendering `From: undefined` when neither field has a value.

- `react/src/components/FolderInvitationResponseModal.tsx` — add `useSuspendedBackendaiClient` + `hasInviterEmail`, render `(hasInviterEmail ? item.inviter_user_email : item.inviter) || '-'` in the "From" descriptions item.
- `react/src/components/SummaryPageItems/SummaryItemInvitation.tsx` — same gating in the card title.

## Known limitation (and the backend follow-up that unblocks it)

The current `VFolderInvitationDTO` (`src/ai/backend/common/dto/manager/vfolder/response.py` in `lablup/backend.ai`) only declares the single `inviter` field:

```python
class VFolderInvitationDTO(BackendAISchema):
    inviter: str = Field(description="Inviter email, or username if email is empty")
    ...
```

The handler collapses both possibilities into that field (`inviter=info.inviter_user_email or info.inviter_username or ""`). So on managers where the capability flag is *true* but the DTO has not yet been expanded with `inviter_user_email`, this PR will render `From: -` for every invitation. Older managers (capability = false) continue to read the legacy `inviter` field and work as before.

The backend side needs to add `inviter_user_email` as a separate DTO field for the capability-gated branch to actually show the email. Tracked separately in [BA-6228](https://lablup.atlassian.net/browse/BA-6228) — until that lands, manager ≥ 25.6.0 will render `From: -` for every invitation. Older managers (capability = false) continue to read the legacy `inviter` field and work as before.

## What this PR explicitly does NOT fix — and why the frontend can't

The SSO-side problem above is **not** addressable from the client:

- The invitation payload contains the empty email itself; there is no other identifying field to fall back to.
- The payload has no `is_sso` / `external_id` flag, so the frontend cannot even differentiate SSO rows to apply a special UI.
- A cosmetic "Unknown sender" fallback would only mask the symptom while leaving the accept-flow exception in BA-6193 untouched.

So the fix has to land where the empty email originates — the backend. See [BA-6193](https://lablup.atlassian.net/browse/BA-6193).

## Verification

- `bash scripts/verify.sh` — Relay, Lint, Format PASS. TypeScript surfaces only pre-existing failures in unrelated files; no new errors in the touched files.
- Manual: invite a manually-created user from another manually-created user on a manager that exposes `inviter_user_email`; the Summary card and the invitation modal both show the inviter's email. On a manager that only returns `inviter` (current main), both fall back to the legacy field and render correctly. SSO-related rows remain blank, expected until BA-6193 lands.

## Checklist

- [x] Documentation — n/a (capability-check pattern already documented by FR-1101's PR)
- [x] Minimum required manager version — no change (the existing capability flag covers both old and new managers)
- [x] Minimum requirements to check during review — log in as the invitee and confirm the Summary invitation card title and the `FolderInvitationResponseModal` "From:" field both show the inviter's email (or `-` if not available on the current manager)
- [ ] Test case(s) — covered by the e2e in #7610 (FR-2982)

[FR-2979]: https://lablup.atlassian.net/browse/FR-2979?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
[BA-6193]: https://lablup.atlassian.net/browse/BA-6193?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

[BA-6228]: https://lablup.atlassian.net/browse/BA-6228?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
@graphite-app graphite-app Bot force-pushed the fix/FR-2979-shared-folder-invite-sender branch from 868c4a8 to 17c6056 Compare May 29, 2026 01:07
@graphite-app graphite-app Bot merged commit 17c6056 into main May 29, 2026
13 checks passed
@graphite-app graphite-app Bot deleted the fix/FR-2979-shared-folder-invite-sender branch May 29, 2026 01:10
graphite-app Bot pushed a commit that referenced this pull request Jun 9, 2026
#7610)

Resolves #7608 (FR-2982)

> Stacked on #7607 — merge that one first.

## Summary
- Extend `acceptAllInvitationAndVerifySpecificFolder` (in `e2e/utils/test-util.ts`) with an optional `inviterEmail` parameter.
- When the parameter is provided, assert that the open `FolderInvitationResponseModal` contains that email before the Accept loop. This catches regressions in the modal's "From" field — i.e. broken `item.inviter_user_email` plumbing or a broken `invitation-inviter-email` capability check.
- Pass `userInfo.user.email` (the inviter set up by `loginAsUser` in `beforeEach`) at the single existing call site in `e2e/vfolder/vfolder-crud.spec.ts` ("User can share vFolder"), so the assertion runs in CI.
- No production source changes; e2e-only.

## Why
- The "From" field is the only place an invitee sees who shared a folder with them. Today no e2e / unit / Storybook test asserts that field is non-empty or correct.
- Existing test already exercises the modal, so the new check is a low-cost extension rather than a new scenario.

## Test plan
- [ ] CI: existing `vfolder/vfolder-crud.spec.ts` "User can share vFolder" run passes with the new assertion enabled.
- [ ] On a backend with `invitation-inviter-email` capability, the assertion confirms `user@lablup.com` is rendered inside the modal.
- [ ] On a backend without that capability, the assertion would fail (the modal falls back to `item.inviter`, a non-email value); intentional — this is the regression we want to surface.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S 10~30 LoC

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants