feat: add emptyState pages and cause-specific messaging when a project has no environments#678
Conversation
Signed-off-by: Akila-I <akila.99g@gmail.com>
- Introduced EnvironmentsStatusNotice component to provide specific feedback on environment loading states. - Updated useProjectEnvironments hook to return a status indicating the reason for empty environments (ok, empty-pipeline, forbidden, unavailable). - Refactored multiple observability components (CostAnalysis, Incidents, Metrics, RCA, RuntimeEvents, RuntimeLogs, Traces, Wirelogs) to utilize the new status handling and display appropriate messages. - Improved test cases to validate the new status responses and ensure correct rendering of notices based on environment states. Signed-off-by: Akila-I <akila.99g@gmail.com>
…notices Signed-off-by: Akila-I <akila.99g@gmail.com>
📝 WalkthroughWalkthroughBackend ChangesBackend and shared environment status contract
Observability pages
Estimated code review effort: 3 (Moderate) | ~30 minutes NoEnvironmentsEmptyState and Deploy/Environments UI
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
|
@CodeRabbit review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (6)
plugins/openchoreo-observability/src/components/common/EnvironmentsStatusNotice.tsx (1)
24-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winLGTM! The status discrimination logic is clean and covers all four cases correctly.
One optional enhancement: the
unavailablebranch (lines 56–62) could wire a retry button. The hooks already exposerefetch, and Backstage'sEmptyStatesupports anactionprop. This would let users retry without a full page refresh.♻️ Optional: add retry action
export interface EnvironmentsStatusNoticeProps { status: ProjectEnvironmentsStatus; feature?: string; + onRetry?: () => void; } export const EnvironmentsStatusNotice = ({ status, feature, + onRetry, }: EnvironmentsStatusNoticeProps) => { // ... return ( <EmptyState missing="data" title="Failed to load environments" description="Couldn't load this project's deployment pipeline. Review the deployment pipeline or contact your administrator." + action={onRetry && <Button onClick={onRetry} variant="contained" color="primary">Retry</Button>} /> ); };🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/openchoreo-observability/src/components/common/EnvironmentsStatusNotice.tsx` around lines 24 - 63, The unavailable EmptyState in EnvironmentsStatusNotice should expose a retry action so users can re-fetch without refreshing the page. Update EnvironmentsStatusNoticeProps usage to pass through the existing refetch handler from the surrounding hooks, and wire it into the final EmptyState via its action prop with a clear retry label. Keep the current status branching intact and only enhance the “unavailable” path.plugins/openchoreo-react/src/hooks/useProjectEnvironments.ts (1)
75-87: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider resetting
statusat the start of each fetch cycle.
loadinganderrorare reset at lines 65–66, butstatusretains its previous value during a refetch. All observed consumers checkloadingfirst, so the stale status is never visible — but resetting it here would make the state transition self-consistent and guard against future consumers that might readstatusbeforeloading.♻️ Optional refactor
setLoading(true); setError(null); + setStatus('ok');🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/openchoreo-react/src/hooks/useProjectEnvironments.ts` around lines 75 - 87, In useProjectEnvironments, the fetch cycle clears loading and error but leaves status stale, so reset status at the start of the same request flow before the fetch result handling. Update the state initialization in the hook’s fetch logic so each refetch begins from a neutral status, keeping status transitions self-consistent alongside the existing loading/error resets.plugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.test.tsx (1)
32-37: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDead
EmptyStatemock in@openchoreo/backstage-plugin-react.The component now imports
EmptyStatefrom@backstage/core-components(line 4), but this mock still definesEmptyStateunder the@openchoreo/backstage-plugin-reactmock. Since the import source changed, this mock is no longer exercised and should be removed to avoid confusion.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.test.tsx` around lines 32 - 37, The EmptyState mock in the test setup is now dead because ProjectEnvironmentsList imports EmptyState from `@backstage/core-components`, not `@openchoreo/backstage-plugin-react`. Remove the unused EmptyState stub from the mock setup for ProjectEnvironmentsList.test.tsx and keep the remaining mock entries aligned with the actual imports used by ProjectEnvironmentsList.plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.tsx (1)
26-26: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
!projectNameguard is effectively dead code.
projectNamefalls back toentity.metadata.name(line 22-23), which is always present on a Backstage catalog entity. The!projectNamebranch can never execute. Only!namespaceNameis a meaningful guard here. Not breaking, but worth simplifying to avoid implyingprojectNamecan be absent.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.tsx` at line 26, The guard in NoEnvironmentsEmptyState is overchecking projectName even though it is always populated from entity.metadata.name. Simplify the early return in the component by removing the dead !projectName branch and keeping only the meaningful namespaceName check, while preserving the existing behavior of the empty state rendering logic.plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.test.tsx (1)
28-96: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd test coverage for missing namespace annotation.
The early-return path when
namespaceNameis absent (line 26 of the component) isn't tested. This path skips the fetch entirely and renders the fallback guidance. Adding a test ensures this guard isn't accidentally removed:Proposed test
+ it('renders fallback guidance when namespace annotation is missing', async () => { + const fetchDeploymentPipeline = jest.fn().mockResolvedValue({ name: 'p' }); + + await renderWith( + makeEntity({ [CHOREO_ANNOTATIONS.PROJECT]: 'my-project' }, 'my-project'), + fetchDeploymentPipeline, + ); + + expect( + await screen.findByText(/Contact your administrator to configure it/), + ).toBeInTheDocument(); + expect(fetchDeploymentPipeline).not.toHaveBeenCalled(); + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.test.tsx` around lines 28 - 96, The NoEnvironmentsEmptyState test suite is missing coverage for the early-return path when the namespace annotation is absent, so add a test in NoEnvironmentsEmptyState.test.tsx that renders via renderWith/makeEntity without CHOREO_ANNOTATIONS.NAMESPACE and verifies the fallback guidance is shown, no pipeline configuration link is rendered, and fetchDeploymentPipeline is not called. Use the existing NoEnvironmentsEmptyState and renderWith helpers to cover this guard path.plugins/openchoreo-observability/src/components/Metrics/ObservabilityMetricsPage.test.tsx (1)
113-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd test coverage for non-
okenvironment statuses.The Incidents test file added cases for both
empty-pipelineandunavailablestates, but the Metrics test only updated existing mocks withstatus: 'ok'. Adding equivalent tests would verify theEnvironmentsStatusNoticerendering path and guard against regressions.Also applies to: 222-222
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@plugins/openchoreo-observability/src/components/Metrics/ObservabilityMetricsPage.test.tsx` at line 113, Add test coverage in ObservabilityMetricsPage.test.tsx for non-ok environment statuses by exercising the same EnvironmentsStatusNotice rendering path used in the Incidents tests. Update or add cases in the ObservabilityMetricsPage test suite to mock both empty-pipeline and unavailable statuses instead of only status: ok, and verify the notice appears for each state so regressions in the environment status handling are caught.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.tsx`:
- Around line 17-44: The NoEnvironmentsEmptyState component shows the fallback
message before fetchDeploymentPipeline finishes because pipelineUrl is initially
undefined. Add an explicit loading state alongside the existing useEffect async
fetch in NoEnvironmentsEmptyState so the fallback text is only rendered after
the request completes or fails, and keep the link hidden while loading. Update
the render logic that currently depends on pipelineUrl to gate the “Contact your
administrator…” text on !loading, using the existing entity, client, and
setPipelineUrl flow.
In
`@plugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.tsx`:
- Around line 182-191: The error UI in ProjectEnvironmentsList, and the same
pattern in ResourceEnvironmentsList and PipelineCanvas, lost the retry
affordance when switching to EmptyState. Update the error branch that renders
EmptyState to include a retry action/button wired to the existing fetchEnvs({
showProgress: true }) flow (or the equivalent fetch/reload handler in each
component), so transient failures can be retried without a full page refresh.
---
Nitpick comments:
In
`@plugins/openchoreo-observability/src/components/common/EnvironmentsStatusNotice.tsx`:
- Around line 24-63: The unavailable EmptyState in EnvironmentsStatusNotice
should expose a retry action so users can re-fetch without refreshing the page.
Update EnvironmentsStatusNoticeProps usage to pass through the existing refetch
handler from the surrounding hooks, and wire it into the final EmptyState via
its action prop with a clear retry label. Keep the current status branching
intact and only enhance the “unavailable” path.
In
`@plugins/openchoreo-observability/src/components/Metrics/ObservabilityMetricsPage.test.tsx`:
- Line 113: Add test coverage in ObservabilityMetricsPage.test.tsx for non-ok
environment statuses by exercising the same EnvironmentsStatusNotice rendering
path used in the Incidents tests. Update or add cases in the
ObservabilityMetricsPage test suite to mock both empty-pipeline and unavailable
statuses instead of only status: ok, and verify the notice appears for each
state so regressions in the environment status handling are caught.
In `@plugins/openchoreo-react/src/hooks/useProjectEnvironments.ts`:
- Around line 75-87: In useProjectEnvironments, the fetch cycle clears loading
and error but leaves status stale, so reset status at the start of the same
request flow before the fetch result handling. Update the state initialization
in the hook’s fetch logic so each refetch begins from a neutral status, keeping
status transitions self-consistent alongside the existing loading/error resets.
In
`@plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.test.tsx`:
- Around line 28-96: The NoEnvironmentsEmptyState test suite is missing coverage
for the early-return path when the namespace annotation is absent, so add a test
in NoEnvironmentsEmptyState.test.tsx that renders via renderWith/makeEntity
without CHOREO_ANNOTATIONS.NAMESPACE and verifies the fallback guidance is
shown, no pipeline configuration link is rendered, and fetchDeploymentPipeline
is not called. Use the existing NoEnvironmentsEmptyState and renderWith helpers
to cover this guard path.
In
`@plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.tsx`:
- Line 26: The guard in NoEnvironmentsEmptyState is overchecking projectName
even though it is always populated from entity.metadata.name. Simplify the early
return in the component by removing the dead !projectName branch and keeping
only the meaningful namespaceName check, while preserving the existing behavior
of the empty state rendering logic.
In
`@plugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.test.tsx`:
- Around line 32-37: The EmptyState mock in the test setup is now dead because
ProjectEnvironmentsList imports EmptyState from `@backstage/core-components`, not
`@openchoreo/backstage-plugin-react`. Remove the unused EmptyState stub from the
mock setup for ProjectEnvironmentsList.test.tsx and keep the remaining mock
entries aligned with the actual imports used by ProjectEnvironmentsList.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 49600339-5556-4ff6-8150-cc412dc38daa
📒 Files selected for processing (38)
.changeset/deploy-backstage-empty-state.md.changeset/observability-env-error-messages.mdplugins/openchoreo-backend/src/services/ProjectService/ProjectInfoService.test.tsplugins/openchoreo-backend/src/services/ProjectService/ProjectInfoService.tsplugins/openchoreo-observability/src/components/Alerts/ObservabilityAlertsPage.test.tsxplugins/openchoreo-observability/src/components/Alerts/ObservabilityAlertsPage.tsxplugins/openchoreo-observability/src/components/CostAnalysis/CostAnalysisPage.test.tsxplugins/openchoreo-observability/src/components/CostAnalysis/CostAnalysisPage.tsxplugins/openchoreo-observability/src/components/Incidents/ObservabilityProjectIncidentsPage.test.tsxplugins/openchoreo-observability/src/components/Incidents/ObservabilityProjectIncidentsPage.tsxplugins/openchoreo-observability/src/components/Metrics/ObservabilityMetricsPage.test.tsxplugins/openchoreo-observability/src/components/Metrics/ObservabilityMetricsPage.tsxplugins/openchoreo-observability/src/components/RCA/RCAPage.test.tsxplugins/openchoreo-observability/src/components/RCA/RCAPage.tsxplugins/openchoreo-observability/src/components/RuntimeEvents/ObservabilityRuntimeEventsPage.test.tsxplugins/openchoreo-observability/src/components/RuntimeEvents/ObservabilityRuntimeEventsPage.tsxplugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.test.tsxplugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityProjectRuntimeLogsPage.tsxplugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.test.tsxplugins/openchoreo-observability/src/components/RuntimeLogs/ObservabilityRuntimeLogsPage.tsxplugins/openchoreo-observability/src/components/Traces/ObservabilityTracesPage.test.tsxplugins/openchoreo-observability/src/components/Traces/ObservabilityTracesPage.tsxplugins/openchoreo-observability/src/components/Wirelogs/ObservabilityWirelogsPage.test.tsxplugins/openchoreo-observability/src/components/Wirelogs/ObservabilityWirelogsPage.tsxplugins/openchoreo-observability/src/components/common/EnvironmentsStatusNotice.tsxplugins/openchoreo-observability/src/components/common/index.tsplugins/openchoreo-observability/src/hooks/useWirelogsEnvironments.tsplugins/openchoreo-react/src/hooks/useProjectEnvironments.test.tsxplugins/openchoreo-react/src/hooks/useProjectEnvironments.tsplugins/openchoreo-react/src/index.tsplugins/openchoreo/src/components/Environments/PipelineDAG/PipelineCanvas.test.tsxplugins/openchoreo/src/components/Environments/PipelineDAG/PipelineCanvas.tsxplugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.test.tsxplugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.tsxplugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.test.tsxplugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.tsxplugins/openchoreo/src/components/ResourceEnvironments/ResourceEnvironments.test.tsxplugins/openchoreo/src/components/ResourceEnvironments/ResourceEnvironmentsList.tsx
| const [pipelineUrl, setPipelineUrl] = useState<string | undefined>(); | ||
|
|
||
| useEffect(() => { | ||
| let cancelled = false; | ||
| const projectName = | ||
| entity.metadata.annotations?.[CHOREO_ANNOTATIONS.PROJECT] ?? | ||
| entity.metadata.name; | ||
| const namespaceName = | ||
| entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE]; | ||
| if (!projectName || !namespaceName) return undefined; | ||
|
|
||
| client | ||
| .fetchDeploymentPipeline(projectName, namespaceName) | ||
| .then((pipeline: { name?: string }) => { | ||
| if (cancelled || !pipeline?.name) return; | ||
| const catalogNamespace = entity.metadata.namespace || 'default'; | ||
| setPipelineUrl( | ||
| `/catalog/${catalogNamespace}/deploymentpipeline/${pipeline.name}`, | ||
| ); | ||
| }) | ||
| .catch(() => { | ||
| // Leave the link out if the pipeline ref can't be resolved. | ||
| }); | ||
|
|
||
| return () => { | ||
| cancelled = true; | ||
| }; | ||
| }, [entity, client]); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Transient fallback text during pipeline fetch.
pipelineUrl starts as undefined, so the component renders "Contact your administrator to configure it." while the async fetch is in-flight. Once the pipeline resolves, the text switches to include the link. This flash of fallback content is misleading — users may read the admin-contact guidance and navigate away before the link appears.
Track a loading state and defer the fallback until the fetch completes:
Proposed fix
export const NoEnvironmentsEmptyState = () => {
const { entity } = useEntity();
const client = useApi(openChoreoClientApiRef);
const [pipelineUrl, setPipelineUrl] = useState<string | undefined>();
+ const [loading, setLoading] = useState(true);
useEffect(() => {
let cancelled = false;
const projectName =
entity.metadata.annotations?.[CHOREO_ANNOTATIONS.PROJECT] ??
entity.metadata.name;
const namespaceName =
entity.metadata.annotations?.[CHOREO_ANNOTATIONS.NAMESPACE];
- if (!projectName || !namespaceName) return undefined;
+ if (!projectName || !namespaceName) {
+ setLoading(false);
+ return undefined;
+ }
client
.fetchDeploymentPipeline(projectName, namespaceName)
.then((pipeline: { name?: string }) => {
if (cancelled || !pipeline?.name) return;
const catalogNamespace = entity.metadata.namespace || 'default';
setPipelineUrl(
`/catalog/${catalogNamespace}/deploymentpipeline/${pipeline.name}`,
);
})
.catch(() => {
// Leave the link out if the pipeline ref can't be resolved.
})
+ .finally(() => {
+ if (!cancelled) setLoading(false);
+ });
return () => {
cancelled = true;
};
}, [entity, client]);Then gate the fallback on !loading:
{pipelineUrl ? (
<>
Review the <Link to={pipelineUrl}>pipeline configuration</Link> or
contact your administrator.
</>
) : (
- <>Contact your administrator to configure it.</>
+ !loading && <>Contact your administrator to configure it.</>
)}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/openchoreo/src/components/Environments/components/NoEnvironmentsEmptyState.tsx`
around lines 17 - 44, The NoEnvironmentsEmptyState component shows the fallback
message before fetchDeploymentPipeline finishes because pipelineUrl is initially
undefined. Add an explicit loading state alongside the existing useEffect async
fetch in NoEnvironmentsEmptyState so the fallback text is only rendered after
the request completes or fails, and keep the link hidden while loading. Update
the render logic that currently depends on pipelineUrl to gate the “Contact your
administrator…” text on !loading, using the existing entity, client, and
setPipelineUrl flow.
| <EmptyState | ||
| missing="data" | ||
| title="Failed to load environments" | ||
| description={getErrorMessage(error)} | ||
| /> | ||
| ); | ||
| } | ||
|
|
||
| if (envs.length === 0) { | ||
| return ( | ||
| <Card style={{ minHeight: '300px', width: '100%' }}> | ||
| <EmptyState | ||
| title="No environments available" | ||
| description="This project's deployment pipeline has no environments configured." | ||
| action={{ | ||
| label: 'Retry', | ||
| onClick: () => fetchEnvs({ showProgress: true }), | ||
| }} | ||
| /> | ||
| </Card> | ||
| ); | ||
| return <NoEnvironmentsEmptyState />; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Error state no longer offers a retry action.
The previous ErrorState included an onRetry that called fetchEnvs({ showProgress: true }). The new EmptyState from @backstage/core-components has no retry affordance, so users hitting a transient fetch failure must refresh the page to recover. Consider adding a retry button or action to the error EmptyState.
This same pattern applies to ResourceEnvironmentsList.tsx and PipelineCanvas.tsx.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@plugins/openchoreo/src/components/ProjectEnvironments/ProjectEnvironmentsList.tsx`
around lines 182 - 191, The error UI in ProjectEnvironmentsList, and the same
pattern in ResourceEnvironmentsList and PipelineCanvas, lost the retry
affordance when switching to EmptyState. Update the error branch that renders
EmptyState to include a retry action/button wired to the existing fetchEnvs({
showProgress: true }) flow (or the equivalent fetch/reload handler in each
component), so transient failures can be retried without a full page refresh.
Purpose
Related: openchoreo/openchoreo#4073
This pull request introduces improved empty and error state handling for the Deploy and Observability tabs by standardizing on Backstage's
EmptyStatecomponent and providing more specific, actionable messaging. The changes affect both frontend and backend, ensuring consistent user experience and better error reporting across the app.User Experience Improvements:
EmptyStatefor empty and error states, replacing custom cards and alerts for a more unified look and feel. [1] [2]statusfromuseProjectEnvironmentsand a sharedEnvironmentsStatusNoticecomponent. [1] [2] [3] [4]Backend and Error Handling:
NotFoundError) when a project is missing a deployment pipeline reference, instead of a generic 500 error. [1] [2]Code and API Changes:
useProjectEnvironmentshook now returns a discriminatedstatus(ok,empty-pipeline,forbidden,unavailable), which is used throughout the Observability UI to determine which empty/error state to display. [1] [2] [3] [4]These changes ensure users receive clear, actionable feedback when environments are missing or misconfigured, and that the UI is consistent across all relevant parts of the application.
Goals
Approach
Screenshots:

Deploy page -
Few Observability Pages (similar behaviour in logs/metrics/traces/alerts/incidents/RCA reports/cost analysis reports/wirelogs) -

User stories
Release note
Documentation
Training
Certification
Marketing
Automation tests
Security checks
Samples
Related PRs
Migrations (if applicable)
Test environment
Learning
Summary by CodeRabbit
New Features
Bug Fixes