Skip to content

feat(search): add useFleetSearchSubscription and useFleetSearch hooks to multicluster-sdk#1

Open
zlayne wants to merge 16 commits into
mainfrom
feat/ACM-32322-fleet-search-subscription
Open

feat(search): add useFleetSearchSubscription and useFleetSearch hooks to multicluster-sdk#1
zlayne wants to merge 16 commits into
mainfrom
feat/ACM-32322-fleet-search-subscription

Conversation

@zlayne

@zlayne zlayne commented Jul 7, 2026

Copy link
Copy Markdown
Owner

📝 Summary

Ticket Summary (Title):
ACM Console - multicluster-sdk should support Search API GraphQL Subscriptions

Ticket Link:
https://redhat.atlassian.net/browse/ACM-32322

Type of Change:

  • 🐞 Bug Fix
  • ✨ Feature
  • 🔧 Refactor
  • 💸 Tech Debt
  • 🧪 Test-related
  • 📄 Docs

What

Adds two new public hooks to the @stolostron/multicluster-sdk package:

useFleetSearchSubscription(input)

Wraps the generated useSearchSubscription Apollo hook with the shared searchClient. Opens a GraphQL WebSocket subscription and streams real-time change events from the search index. Returns [latestEvent, loading, error] — only the most recent Event is returned (not accumulated); callers react with a useEffect watching latestEvent. Skips the WebSocket connection entirely when input is undefined.

useFleetSearch<T>(input, subscriptionEnabled?)

Combines a one-shot Apollo HTTP query (useSearchResultItemsQuery) with an optional real-time subscription layer. When subscriptionEnabled is true, the hook opens a WebSocket subscription via useFleetSearchSubscription and patches locally-held results on INSERT, UPDATE, and DELETE events — keeping the data always up to date without polling. Pagination is supported by setting limit and offset on the SearchInput object.

Also re-exports SearchInput from src/types/search.ts so callers can type their input without reaching into internal packages.

Why

The Search API already supports GraphQL subscriptions over WebSocket, but the multicluster-sdk had no public hook to consume them. This meant search results became stale and required polling (or full page refreshes) to stay current. With these hooks, consumers get live updates as resources are created, updated, or deleted in the search index.

How to test

  1. npm run setup (if not already done)
  2. npm run plugins — start in plugin mode
  3. Navigate to http://localhost:9000/multicloud/search
  4. Verify the Search page loads with results (HTTP transport — useFleetSearch base query)
  5. To verify the WebSocket transport, open DevTools → Network → WS — confirm a WebSocket connection is opened to /proxy/search when a subscription-enabled consumer is rendered

Unit tests (automated)

cd frontend/packages/multicluster-sdk
npm test -- --testPathPattern="useFleetSearch"
# 60 tests: 12 useFleetSearchSubscription + 30 useFleetSearch + 18 existing useFleetSearchPoll (unaffected)

E2E test (Playwright, run against cluster)

OCP_API_URL=<cluster-api-url> OCP_TOKEN=<token> RHACM_URL=<console-url> \
  npx playwright test e2e-template/ACM-32322-fleet-search-subscription.spec.ts --browser=chromium
# 2 passed: HTTP transport + WebSocket endpoint reachable

✅ Checklist

General

  • PR title follows the convention
  • Code builds and runs locally without errors
  • No console logs, commented-out code, or unnecessary files
  • All commits are meaningful and well-labeled
  • All new display strings are externalized for localization (English only) — no user-facing strings added
  • JSDoc comments added for new functions (full TSDoc with @param, @returns, @example)

If Feature

  • All acceptance criteria met
  • Unit test coverage added (42 new tests, 100% coverage of new APIs)
  • README.md API docs regenerated (npm run generate-doc)

🗒️ Notes for Reviewers

  • useFleetSearchSubscription is the low-level primitive — it returns only the latest event (not an array). Callers accumulate events via useEffect if needed.
  • useFleetSearch uses useState initialised from the query result and patches it in-place on each WebSocket event. When subscriptionEnabled flips to false, it resets to the base query data.
  • The eslint-disable-next-line react-hooks/exhaustive-deps comment on the subscriptionEnabled toggle effect is intentional — we only want to reset when the flag changes, not on every queryData change (which has its own effect).
  • convertSearchItemToResource cast uses as unknown as SearchResult<T> — same pattern as useFleetSearchPoll — due to the conditional mapped type not being narrowable at the call site.

zlayne added 16 commits July 7, 2026 09:53
… to multicluster-sdk

ACM-32322 — Add GraphQL subscription support to the multicluster-sdk.

- useFleetSearchSubscription: wraps the generated useSearchSubscription
  Apollo hook; returns [latestEvent, loading, error]; skips the WebSocket
  connection when input is undefined.
- useFleetSearch: one-shot query hook with optional real-time patching via
  useFleetSearchSubscription; handles INSERT/UPDATE/DELETE events; supports
  pagination through SearchInput.limit and SearchInput.offset.
- Re-export SearchInput from src/types/search.ts so callers can type their
  input without reaching into internal packages.
- Update src/api/index.ts and src/index.test.ts public-API guard.
- Regenerate README.md API docs.

Signed-off-by: zlayne <zlayne@redhat.com>
Verifies:
- Search page loads and returns results via Apollo HTTP link
- Search WebSocket proxy endpoint is reachable (WS transport for subscriptions)

Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
Signed-off-by: zlayne <zlayne@redhat.com>
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