Skip to content
Open
5 changes: 5 additions & 0 deletions .changeset/scheduled-task-runs-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openchoreo/backstage-plugin-openchoreo-observability': minor
---

Add Runs tab to scheduled-task component entity pages. Renders a Component → Runs (Jobs) → Retries (Pods) → Logs hierarchy over the observer's `/scheduled-tasks/runs/query` and `/scheduled-tasks/runs/{jobName}/retries/query` endpoints. Retries queries are scoped to each run's own lifetime via optional `startTime` / `endTime`, avoiding the observer's per-call event cap on high-frequency CronJobs. Requires the observer backend from openchoreo/openchoreo#3933.
92 changes: 90 additions & 2 deletions packages/app/src/components/catalog/EntityPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ import {
ObservabilityWirelogs,
ObservabilityProjectIncidents,
ObservabilityCostAnalysis,
ObservabilityRuns,
useComponentHasAnyCiliumEnabledEnvironment,
type RenderLogRowAction,
} from '@openchoreo/backstage-plugin-openchoreo-observability';
Expand Down Expand Up @@ -454,6 +455,84 @@ const ServiceEntityPage = () => {
);
};

/**
* Scheduled task entity page with delete menu support.
* Adds a Runs tab that shows Job/Pod execution history.
* No API tab or Alerts tab (not relevant for CronJobs).
*/
const scheduledTaskEntityPage = (
<EntityLayoutWithDelete>
<EntityLayout.Route path="/" title="Overview">
<OverviewContent />
</EntityLayout.Route>

<EntityLayout.Route path="/definition" title="Definition">
<ResourceDefinitionTab />
</EntityLayout.Route>

<EntityLayout.Route path="/workflows" title="Build">
<FeatureGatedContent feature="workflows">
<Workflows />
</FeatureGatedContent>
</EntityLayout.Route>

<EntityLayout.Route path="/environments" title="Deploy">
<Environments />
</EntityLayout.Route>

<EntityLayout.Route path="/runs" title="Runs">
<FeatureGatedContent feature="observability">
<ObservabilityRuns />
</FeatureGatedContent>
</EntityLayout.Route>

<EntityLayout.Route path="/runtime-logs" title="Logs">
<FeatureGatedContent feature="observability">
<ObservabilityRuntimeLogs />
</FeatureGatedContent>
</EntityLayout.Route>

<EntityLayout.Route path="/metrics" title="Metrics">
<FeatureGatedContent feature="observability">
<ObservabilityMetrics />
</FeatureGatedContent>
</EntityLayout.Route>

<EntityLayout.Route
path="/kubernetes"
title="Kubernetes"
if={isKubernetesAvailable}
>
<EntityKubernetesContent />
</EntityLayout.Route>

<EntityLayout.Route path="/docs" title="Docs" if={hasTechdocsAnnotation}>
{techdocsContent}
</EntityLayout.Route>

{/* External CI Platform Tabs - only shown when annotation is present */}
<EntityLayout.Route
path="/jenkins"
title="Jenkins"
if={hasJenkinsAnnotation}
>
<EntityJenkinsContent />
</EntityLayout.Route>

<EntityLayout.Route
path="/github-actions"
title="GitHub Actions"
if={hasGithubActionsAnnotation}
>
<EntityGithubActionsContent />
</EntityLayout.Route>

<EntityLayout.Route path="/gitlab" title="GitLab" if={hasGitlabAnnotation}>
<EntityGitlabContent />
</EntityLayout.Route>
</EntityLayoutWithDelete>
);

/**
* Website entity page with delete menu support.
* Routes are defined as static JSX children so routable extensions are discoverable.
Expand Down Expand Up @@ -618,15 +697,24 @@ function getComponentPageVariant(entity: Entity): PageVariant {
const isServiceComponent = (entity: Entity) =>
getComponentPageVariant(entity) === 'service';

const isGenericComponent = (entity: Entity) =>
getComponentPageVariant(entity) !== 'service';
const isScheduledTaskComponent = (entity: Entity) =>
getComponentPageVariant(entity) === 'scheduled-task';

const isGenericComponent = (entity: Entity) => {
const variant = getComponentPageVariant(entity);
return variant !== 'service' && variant !== 'scheduled-task';
};

const componentPage = (
<EntitySwitch>
<EntitySwitch.Case if={isServiceComponent}>
<ServiceEntityPage />
</EntitySwitch.Case>

<EntitySwitch.Case if={isScheduledTaskComponent}>
{scheduledTaskEntityPage}
</EntitySwitch.Case>

<EntitySwitch.Case if={isGenericComponent}>
<GenericComponentEntityPage />
</EntitySwitch.Case>
Expand Down
Loading
Loading