Skip to content
Closed
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: 49 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Repository Guidelines

## Project Structure & Module Organization
This repository is a multi-module project:
- `kafka-ui-api`: Spring Boot backend (`src/main/java`, `src/test/java`)
- `kafka-ui-contract`: shared API contract and generated clients
- `kafka-ui-serde-api`: serialization/deserialization API module
- `kafka-ui-react-app`: React + TypeScript frontend (`src/components`, `src/lib`, `src/generated-sources`)
- `kafka-ui-e2e-checks`: end-to-end UI checks (Maven/TestNG suites)
- `documentation/`, `etc/`, `.github/workflows/`: docs, style config, CI

## Build, Test, and Development Commands
- `./mvnw -B -V -ntp verify`: build and run backend/module checks.
- `./mvnw clean install`: build all modules.
- `./mvnw clean install -DskipTests`: build all modules without tests.
- `./mvnw -pl kafka-ui-api test`: run backend tests for API module only.
- `./mvnw spring-boot:run -pl kafka-ui-api`: run backend locally.
- `./mvnw spring-boot:run -pl kafka-ui-api -Dspring-boot.run.profiles=local`: run backend with local profile.
- `cd kafka-ui-react-app && pnpm install --frozen-lockfile`: install frontend deps.
- `cd kafka-ui-react-app && pnpm gen:sources`: regenerate OpenAPI TS clients.
- `cd kafka-ui-react-app && pnpm dev`: run frontend locally (Vite).
- `cd kafka-ui-react-app && pnpm build`: build frontend.
- `cd kafka-ui-react-app && pnpm lint && pnpm test`: run frontend lint/tests.
- `cd kafka-ui-react-app && pnpm lint:CI && pnpm test:CI`: CI-equivalent frontend lint/tests.
- `./mvnw -f kafka-ui-e2e-checks test -Pprod`: run e2e checks.
- `docker-compose -f ./documentation/compose/kafka-ui.yaml up`: run local Kafka + UI stack.

## Node.js Version Handling
- If a specific Node.js version is required, install it with `nvm install <version>`.
- Activate it with `nvm use <version>`.
- After UAT verification, notify the user.
- Do not uninstall Node.js versions automatically; the user will uninstall the version manually.

## Coding Style & Naming Conventions
- Follow `.editorconfig`: UTF-8, LF, trailing newline, default 4 spaces; Java uses 2-space indentation.
- Java style is enforced via `etc/checkstyle.xml`.
- Frontend uses ESLint + Prettier (`kafka-ui-react-app/.eslintrc.json`, `.prettierrc`).
- Prefer `camelCase` for variables/query params, `PascalCase` for React components, and lowercase plural REST paths (hyphen-separated segments).

## Testing Guidelines
- Backend: JUnit 5 + Testcontainers under `kafka-ui-api/src/test/java`.
- Frontend: Jest + Testing Library, tests named `*.spec.ts` / `*.spec.tsx` in `__test__` or `__tests__`.
- Run affected module tests locally before opening a PR; keep CI checks green.

## Commit & Pull Request Guidelines
- Match existing commit style: concise imperative subject, often scoped (`BE: ...`, `FE: ...`) and issue reference like `(#4427)`.
- Use GitHub closing keywords in commits/PR description when applicable.
- PRs must include: clear summary, testing notes, linked issues, matching labels, and docs/env var updates when behavior changes.
- Complete `.github/PULL_REQUEST_TEMPLATE.md` checklist to satisfy PR automation.
259 changes: 259 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

UI for Apache Kafka is a free, open-source web UI to monitor and manage Apache Kafka clusters. Built with Spring Boot (reactive WebFlux) backend and React/TypeScript frontend.

**Key Technologies:**
- Backend: Java 17, Spring Boot 3.1.3, Spring WebFlux (reactive), Kafka clients 3.5.0
- Frontend: React 18, TypeScript, Vite, Redux Toolkit, TanStack React Query
- Build: Maven (backend), pnpm (frontend)
- Testing: JUnit 5, Testcontainers (backend), Jest (frontend), Selenide/TestNG (E2E)

## Build & Development Commands

### Backend (from project root)

```bash
# Build all modules
./mvnw clean install

# Build without tests
./mvnw clean install -DskipTests

# Run tests for specific module
./mvnw test -pl kafka-ui-api

# Run the application (requires Kafka cluster configured)
./mvnw spring-boot:run -pl kafka-ui-api

# Run with specific profile
./mvnw spring-boot:run -pl kafka-ui-api -Dspring-boot.run.profiles=local

# Check code style
./mvnw checkstyle:check
```

### Frontend (from kafka-ui-react-app/)

```bash
# Install dependencies
pnpm install

# Generate API clients from OpenAPI spec (required before first run)
pnpm gen:sources

# Start development server
pnpm dev

# Build for production
pnpm build

# Run tests
pnpm test

# Run tests with coverage
pnpm test:coverage

# Lint code
pnpm lint

# Fix linting issues
pnpm lint:fix

# Type check
pnpm tsc
```

### Docker Development

```bash
# Start local Kafka cluster with UI
docker-compose -f ./documentation/compose/kafka-ui.yaml up

# For frontend development with proxied backend
# Set VITE_DEV_PROXY in kafka-ui-react-app/.env.local
# then run: pnpm dev
```

### E2E Tests (from kafka-ui-e2e-checks/)

```bash
# Run E2E tests
./mvnw test
```

## Node.js Version Handling

- If a specific Node.js version is required, install it with `nvm install <version>`.
- Activate it with `nvm use <version>`.
- After UAT verification, notify the user.
- Do not uninstall Node.js versions automatically; the user will uninstall the version manually.

## Project Structure

### Maven Modules

- **kafka-ui-contract**: OpenAPI spec (`src/main/resources/swagger/kafka-ui-api.yaml`) and generated API interfaces/DTOs
- Generates server-side API interfaces for backend
- Generates TypeScript client for frontend (via pnpm gen:sources)

- **kafka-ui-api**: Main Spring Boot application
- Entry point: `KafkaUiApplication.java`
- Controllers: Implement generated API interfaces from kafka-ui-contract
- Services: Business logic for Kafka operations
- Emitters: Reactive message streaming components
- Serdes: Built-in serialization/deserialization implementations

- **kafka-ui-serde-api**: Plugin API for custom serialization/deserialization
- Interface for extending with custom serdes (Avro, Protobuf, etc.)

- **kafka-ui-e2e-checks**: End-to-end UI tests using Selenide and TestNG

- **kafka-ui-react-app**: React frontend application
- Generated API clients in `src/generated-sources/` (from OpenAPI spec)
- State management: Redux Toolkit + TanStack React Query
- UI components in `src/components/`
- Route configuration in `src/`

### Key Backend Packages

- `com.provectus.kafka.ui.controller`: REST API controllers (implement generated interfaces)
- `com.provectus.kafka.ui.service`: Business logic layer
- `com.provectus.kafka.ui.service.rbac`: Role-based access control
- `com.provectus.kafka.ui.service.metrics`: Kafka metrics collection
- `com.provectus.kafka.ui.emitter`: Reactive Kafka message emitters
- `com.provectus.kafka.ui.serdes`: Message serialization/deserialization
- `com.provectus.kafka.ui.config`: Spring configuration including auth, LDAP, OAuth
- `com.provectus.kafka.ui.model`: Domain models
- `com.provectus.kafka.ui.mapper`: MapStruct mappers for DTO conversions

## API Contract Workflow

The project uses code-first OpenAPI approach:

1. OpenAPI spec is manually maintained at `kafka-ui-contract/src/main/resources/swagger/kafka-ui-api.yaml`
2. Maven build generates:
- Java interfaces and DTOs for backend (in kafka-ui-contract module)
- TypeScript client for frontend (consumed by pnpm gen:sources)
3. Backend controllers implement the generated interfaces
4. Frontend uses generated TypeScript client for API calls

**When modifying APIs:**
1. Update the OpenAPI YAML spec in kafka-ui-contract
2. Run `./mvnw clean install` to regenerate Java code
3. Run `pnpm gen:sources` in kafka-ui-react-app to regenerate TypeScript client
4. Update controller implementations and frontend code

## Architecture Notes

### Backend

- **Reactive**: Uses Spring WebFlux with Project Reactor (Mono/Flux) for non-blocking operations
- **Multi-cluster**: Supports managing multiple Kafka clusters via configuration
- **Dynamic config**: Supports runtime configuration via `DYNAMIC_CONFIG_ENABLED` environment variable
- **Serdes**: Pluggable serialization system supporting Avro, Protobuf, JSON Schema, and custom implementations
- **RBAC**: Fine-grained role-based access control for resources
- **Auth**: OAuth2 (GitHub/GitLab/Google), LDAP support

### Frontend

- **React Query**: Used for server state management (API calls, caching)
- **Redux Toolkit**: Used for client state management
- **Code Generation**: TypeScript API client auto-generated from OpenAPI spec
- **Styling**: Styled-components and SASS
- **Forms**: React Hook Form with Yup validation
- **Routing**: React Router v6

## Testing

### Backend Tests

- Unit tests: Standard JUnit 5 tests in `src/test/java`
- Integration tests: Use Testcontainers to spin up Kafka clusters
- Base class: `AbstractIntegrationTest` provides common setup for integration tests
- Run specific test: `./mvnw test -Dtest=ClassName#methodName -pl kafka-ui-api`

### Frontend Tests

- Jest with React Testing Library
- Tests colocated with components
- Run in watch mode: `pnpm test`
- Coverage report: `pnpm test:coverage`

### E2E Tests

- Selenide for browser automation
- TestNG for test orchestration
- Uses docker-compose to spin up full environment
- Located in kafka-ui-e2e-checks module

## Code Style

### Backend

- Checkstyle configuration: `etc/checkstyle/checkstyle.xml`
- Can be imported into IntelliJ IDEA via Checkstyle plugin
- Enforced during build with `./mvnw checkstyle:check`

### Frontend

- ESLint with Airbnb config and TypeScript support
- Prettier for formatting
- Configuration in `.eslintrc.json` and `.prettierrc`

## Naming Conventions

- **REST paths**: lowercase, plural nouns, hyphens for multi-word segments (e.g., `/api/clusters/{clusterName}/consumer-groups`)
- **Query parameters**: camelCase
- **Model names**: camelCase, plural nouns
- **Java**: Standard Java conventions (PascalCase classes, camelCase methods/fields)
- **TypeScript**: PascalCase for components/types, camelCase for functions/variables

## Configuration

Application configuration via:
- `application.yaml` in kafka-ui-api/src/main/resources
- Environment variables (see docs.kafka-ui.provectus.io for full list)
- Dynamic config file when `DYNAMIC_CONFIG_ENABLED=true`

## Groovy Script Execution

Groovy script executions can be controlled via the environment variable:
- `filtering.groovy.enabled`: Enable/disable groovy script executions (security feature)

## Important Files

- `pom.xml`: Root Maven configuration with dependency versions
- `kafka-ui-contract/src/main/resources/swagger/kafka-ui-api.yaml`: OpenAPI specification
- `kafka-ui-api/src/main/java/com/provectus/kafka/ui/KafkaUiApplication.java`: Application entry point
- `kafka-ui-react-app/package.json`: Frontend dependencies and scripts
- `kafka-ui-react-app/vite.config.ts`: Vite build configuration
- `documentation/compose/`: Docker compose examples for various setups

## Common Development Tasks

**Adding a new API endpoint:**
1. Define endpoint in `kafka-ui-contract/src/main/resources/swagger/kafka-ui-api.yaml`
2. Regenerate code: `./mvnw clean install -pl kafka-ui-contract`
3. Implement in appropriate controller in kafka-ui-api
4. Add service layer logic
5. Regenerate frontend client: `cd kafka-ui-react-app && pnpm gen:sources`
6. Use generated client in frontend

**Adding a new custom serde:**
1. Implement `com.provectus.kafka.ui.serde.api.Serde` interface from kafka-ui-serde-api
2. Register in service loader or via configuration
3. Package as plugin JAR if external

**Running single backend test:**
```bash
./mvnw test -Dtest=TestClassName -pl kafka-ui-api
```

**Debugging frontend API calls:**
- API client is in `kafka-ui-react-app/src/generated-sources/`
- Check browser DevTools Network tab
- Backend API base path: `/api`
3 changes: 1 addition & 2 deletions kafka-ui-api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#FROM azul/zulu-openjdk-alpine:17-jre-headless
FROM azul/zulu-openjdk-alpine@sha256:a36679ac0d28cb835e2a8c00e1e0d95509c6c51c5081c7782b85edb1f37a771a
FROM azul/zulu-openjdk-alpine:17.0.13-17.54-jre-headless

RUN apk add --no-cache \
# snappy codec
Expand Down
3 changes: 0 additions & 3 deletions kafka-ui-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -169,19 +169,16 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>${byte-buddy.version}</version>
<scope>test</scope>
</dependency>
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.provectus.kafka.ui.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpHeaders;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;

@Configuration
public class SecurityHeadersConfiguration {

@Bean
public WebFilter securityHeadersFilter() {
return (final ServerWebExchange ctx, final WebFilterChain chain) -> {
final HttpHeaders headers = ctx.getResponse().getHeaders();
headers.add("X-Content-Type-Options", "nosniff");
headers.add("X-Frame-Options", "DENY");
headers.add("X-XSS-Protection", "1; mode=block");
headers.add("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
headers.add("Referrer-Policy", "strict-origin-when-cross-origin");
headers.add("Permissions-Policy", "camera=(), microphone=(), geolocation=()");
headers.add("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; "
+ "img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none'");
return chain.filter(ctx);
};
}

}
15 changes: 15 additions & 0 deletions kafka-ui-api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,18 @@
server:
reactive:
session:
cookie:
http-only: true
secure: true
same-site: lax
compression:
enabled: false
forward-headers-strategy: framework

spring:
jackson:
default-property-inclusion: always

auth:
type: DISABLED

Expand Down
3 changes: 2 additions & 1 deletion kafka-ui-react-app/src/components/ConsumerGroups/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ const List = () => {
header: 'Consumer Lag',
accessorKey: 'consumerLag',
cell: (args) => {
return args.getValue() || 'N/A';
const value = args.getValue();
return value ?? 'N/A';
},
},
{
Expand Down
Loading
Loading