Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 42 additions & 7 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
- **What**: Dual frontend plugins for OpenShift observability (monitoring-plugin + monitoring-console-plugin)
- **Purpose**: Alerts, Metrics, Targets, Dashboards + Perses, Incidents, ACM integration
- **Tech Stack**: React + TypeScript + Webpack + i18next + Go
- **Key Files**: `config/*.patch.json`, `web/src/components/`
- **Key Files**: `config/*.patch.json`, `web/src/features/`, `web/src/shared/`

## Common Tasks & Workflows

### Adding a New Feature

1. Check if it belongs in `monitoring-plugin` (core) or `monitoring-console-plugin` (extended)
2. Add or update the relevant patch file in `config/` (e.g. `config/alerting.patch.json`)
3. Add React components in `web/src/components/`
3. Add React components in `web/src/features/` (feature-specific) or `web/src/shared/` (shared)
4. Add translations in `public/locales/`
5. Test with `make lint-frontend && make test-backend`

Expand All @@ -22,7 +22,7 @@
- **Build failures**: Check `Makefile` targets
- **Console integration**: Verify the relevant `config/*.patch.json` patch file
- **Plugin loading**: Check OpenShift Console logs
- **Perses dashboards**: Debug at `web/src/components/dashboards/perses/`
- **Perses dashboards**: Debug at `web/src/features/perses-dashboards/`

### Development Setup

Expand All @@ -33,14 +33,14 @@

### When working on Alerts:

- Files: `web/src/components/alerts/`
- Files: `web/src/features/alerts/`
- Integration: Alertmanager API
- Testing: Cypress tests in `web/cypress/`

### When working on Dashboards:

- **Legacy**: Standard OpenShift dashboards
- **Perses**: `web/src/components/dashboards/perses/` (uses ECharts wrapper)
- **Perses**: `web/src/features/perses-dashboards/` (uses ECharts wrapper)
- **Upstream**: https://github.com/perses/perses

### When working on ACM:
Expand All @@ -62,6 +62,41 @@
- Verify i18next translation support
- Consider CMO vs COO deployment differences

## Frontend Source Layout

The `web/src/` directory is organized into two top-level areas:

- **`web/src/features/`** — Feature-specific code, one subdirectory per feature. Each feature directory contains sub-folders as needed: `components/`, `pages/`, `hooks/`, `utils/`, `types/`, `assets/`.
- **`web/src/shared/`** — Code used by more than one feature, organized by kind.

| Feature | Directory |
| ------- | --------- |
| Alerting (alerts, silences, alert rules) | `features/alerts/` |
| Incidents | `features/incidents/` |
| Legacy Dashboards | `features/legacy-dashboards/` |
| Metrics / PromQL | `features/metrics/` |
| Perses Dashboards | `features/perses-dashboards/` |
| Targets | `features/targets/` |

| Shared Directory | Contents |
| ---------------- | -------- |
| `shared/components/` | Reusable UI components (`labels.tsx`, `format.tsx`, `query-browser/`) |
| `shared/hooks/` | Shared React hooks (`useAlerts.ts`, `usePerspective.tsx`) |
| `shared/store/` | Redux store, actions, reducers, thunks, alert fetching |
| `shared/contexts/` | React contexts (`MonitoringContext.tsx`) |
| `shared/constants/` | Shared constants (`data-test.ts`, `query-params.ts`) |
| `shared/types/` | Shared TypeScript types (`types.ts`) |
| `shared/utils/` | Shared pure utility functions (`utils.ts`) |
| `shared/assets/` | Static assets such as fonts (`codicon.ttf`) |
| `shared/console/` | Vendored/adapted OpenShift Console internals |

**Placement rules:**
- If a file is only used within one feature, it belongs in that feature's directory.
- If a component or utility is only used by a single page, co-locate it inside that page's own subdirectory (e.g. `features/alerts/pages/alerts-page/AggregateAlertTableRow.tsx`).
- If a file is used across multiple features, it belongs in `shared/`.

> **Note**: `web/src/components/` no longer exists. Do not create files there.

## External Dependencies & Operators

| System | Repository | Purpose |
Expand Down Expand Up @@ -160,7 +195,7 @@ Unit tests focus on isolated function testing and run quickly in CI/CD pipelines

**Backend Tests:**

- **Location**: Co-located with source files in `pkg/`
- **Location**: Co-located with source files in `pkg/` (sub-packages: `pkg/server/`, `pkg/monitoring/`)
- **Naming**: `*_test.go` (e.g., `server_test.go`)
- **Framework**: Go testing package + testify/require
- **Configuration**: Standard Go test conventions
Expand Down Expand Up @@ -320,7 +355,7 @@ npx cypress run --component --spec cypress/component/labels.cy.tsx
Component test files use the `.cy.tsx` extension and go in `web/cypress/component/`:

```typescript
import { MyComponent } from '../../src/components/MyComponent';
import { MyComponent } from '../../src/shared/components/MyComponent';

describe('MyComponent', () => {
it('renders correctly', () => {
Expand Down
40 changes: 37 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,40 @@ changes
| Tests | `.spec.ts` suffix | `MetricsPage.spec.tsx`, `safe-fetch-hook.spec.ts`, `format.spec.ts`, `utils.spec.ts` |
| Styles | `.scss` suffix matching component | `query-browser.scss` |

#### File Placement

The frontend source is split into two top-level directories under `web/src/`:

- **`web/src/features/`** — one subdirectory per product feature. Each feature directory follows an internal structure of `components/`, `pages/`, `hooks/`, `utils/`, `types/`, and `assets/` as needed. New feature-specific code goes here.
- **`web/src/shared/`** — code that is used by more than one feature. Organized by kind:

| Directory | Contents | Example files |
| --------- | -------- | ------------- |
| `shared/components/` | Reusable UI components | `labels.tsx`, `format.tsx`, `query-browser/query-browser.tsx` |
| `shared/hooks/` | Shared custom hooks | `useBoolean.ts`, `useAlerts.ts`, `usePerspective.tsx` |
| `shared/store/` | Redux store, actions, reducers, thunks | `store.ts`, `actions.ts`, `reducers.ts` |
| `shared/contexts/` | React contexts | `MonitoringContext.tsx` |
| `shared/constants/` | Shared constants | `data-test.ts`, `query-params.ts` |
| `shared/types/` | Shared TypeScript types | `types.ts` |
| `shared/utils/` | Shared pure utility functions | `utils.ts`, `react-router-7-adapter.ts` |
| `shared/assets/` | Static assets (fonts, images) | `codicon.ttf` |
| `shared/console/` | Vendored/adapted OpenShift Console internals | `utils/safe-fetch-hook.ts`, `graphs/` |

**Decision rules**:
- If a file is only ever imported by code inside a single feature directory, it belongs in that feature. If it is (or may be) imported by multiple features, it belongs in `shared/`.
- If a component or utility is only used by a single page, it should be co-located inside that page's own subdirectory rather than the feature-level `components/` or `utils/` folder. For example, `AggregateAlertTableRow.tsx` lives inside `features/alerts/pages/alerts-page/` because it is only used by `AlertsPage.tsx`. A page that has page-scoped files should be promoted to its own subdirectory (e.g. `alerts-page/AlertsPage.tsx`) rather than being a standalone file at the `pages/` level.

The current features are:

| Feature | Directory |
| ------- | --------- |
| Alerting (alerts, silences, alert rules) | `features/alerts/` |
| Incidents | `features/incidents/` |
| Legacy Dashboards | `features/legacy-dashboards/` |
| Metrics / PromQL | `features/metrics/` |
| Perses Dashboards | `features/perses-dashboards/` |
| Targets | `features/targets/` |

#### Types and Interfaces

| Type | Convention | Example |
Expand Down Expand Up @@ -237,7 +271,7 @@ export const AlertResource: MonitoringResource = {

#### Test IDs

Use the centralized `DataTestIDs` object in `web/src/components/data-test.ts`:
Use the centralized `DataTestIDs` object in `web/src/shared/constants/data-test.ts`:

```typescript
// ✅ Good: Test ID definitions
Expand Down Expand Up @@ -342,13 +376,13 @@ type RequiredFields = Required<Pick<Config, "name" | "url">>;

### Go Backend Guidelines

The backend that serves plugin assets and proxies APIs is written in Go (see `/cmd` and `/pkg`).
The backend that serves plugin assets and proxies APIs is written in Go (see `/cmd` and `/pkg`). The main packages under `pkg/` are `pkg/server/` (HTTP server and plugin handler) and `pkg/monitoring/` (proxy and API sanitization).
Follow these Go-specific conventions in addition to the general naming rules:

#### Files and Packages

- **File names**: lowercase with underscores only when required (e.g., `plugin_handler.go`, `server_test.go`).
- **Packages**: short, all lowercase, no underscores or mixedCaps (e.g., `proxy`, `handlers`).
- **Packages**: short, all lowercase, no underscores or mixedCaps (e.g., `monitoring`, `server`).
- **Tests**: co-locate `_test.go` files next to the implementation and keep table-driven tests when feasible.

#### Variables and Constants
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This plugin enables frontend UI based on feature flags passed to the backend. Th

## Feature Flags

Feature flags should be added to the Feature enum [here](pkg/server.go) and to the useFeature hook [here](web/src/components/hooks/useFeatures.ts). Whenever a feature is enabled, a set of related feature extension points is included in the plugin-manifest.json served by the backend. These feature extension points are created through the use of [json-patches](https://datatracker.ietf.org/doc/html/rfc6902), such as the `acm-alerting` patch [here](config/acm-alerting.patch.json). The server looks for a patch in the format of `{feature-flag-name}.patch.json` to apply. Some feature flags, such as `acm-alerting` require other flags to be set such as `alertmanager` and `thanos-querier` to instruct the backend how to communicate with the observability signals they utilize
Feature flags should be added to the Feature enum [here](pkg/server/server.go) and to the useFeatures hook [here](web/src/shared/hooks/useFeatures.ts). Whenever a feature is enabled, a set of related feature extension points is included in the plugin-manifest.json served by the backend. These feature extension points are created through the use of [json-patches](https://datatracker.ietf.org/doc/html/rfc6902), such as the `acm-alerting` patch [here](config/acm-alerting.patch.json). The server looks for a patch in the format of `{feature-flag-name}.patch.json` to apply. Some feature flags, such as `acm-alerting` require other flags to be set such as `alertmanager` and `thanos-querier` to instruct the backend how to communicate with the observability signals they utilize

| Feature | OCP Version |
|-------------------|-------------|
Expand Down
2 changes: 1 addition & 1 deletion web/cypress/CYPRESS_TESTING_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ Component tests mount individual React components in isolation using Cypress, wi
Component test files use the `.cy.tsx` extension and live in `cypress/component/`:

```typescript
import { Labels } from '../../src/components/labels';
import { Labels } from '../../src/shared/components/labels';

describe('Labels', () => {
it('renders "No labels" when labels is empty', () => {
Expand Down
18 changes: 15 additions & 3 deletions web/src/shared/hooks/useFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,35 @@ import { useState, useRef, useCallback, useEffect } from 'react';
import { consoleFetchJSON } from '@openshift-console/dynamic-plugin-sdk';

type features = {
alerting: boolean;
'acm-alerting': boolean;
'perses-dashboards': boolean;
'legacy-dashboards': boolean;
metrics: boolean;
targets: boolean;
'cluster-health-analyzer': boolean;
incidents: boolean;
};

type FeaturesResponse = {
alerting?: boolean;
'acm-alerting'?: boolean;
'perses-dashboards'?: boolean;
'legacy-dashboards'?: boolean;
metrics?: boolean;
targets?: boolean;
'cluster-health-analyzer'?: boolean;
incidents?: boolean;
};
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const noFeatures: features = {
alerting: false,
'acm-alerting': false,
'perses-dashboards': false,
'legacy-dashboards': false,
metrics: false,
targets: false,
'cluster-health-analyzer': false,
incidents: false,
};
// monitoring-console-plugin proxy via. cluster observability operator
Expand Down Expand Up @@ -47,8 +62,5 @@ export const useFeatures = () => {

return {
features,
isAcmAlertingActive: features['acm-alerting'],
arePersesDashboardsActive: features['perses-dashboards'],
areIncidentsActive: features.incidents,
};
};