# CLAUDE.md

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

## Build & Test Commands

```bash
./mvnw compile -q              # Compile main sources
./mvnw test-compile -q         # Compile main + test sources
./mvnw test                    # Run all tests (requires Docker for Testcontainers)
./mvnw test -pl . -Dtest=ShareControllerHttpTest  # Run a single test class
./mvnw test -pl . -Dtest="ShareControllerHttpTest#testName"  # Run a single test method
./mvnw clean package           # Full build with packaging
```

Tests use Testcontainers (MySQL, Redis) — Docker must be running. There is no separate lint command; compilation is the primary check.

## Architecture

Spring Boot 3.5 / Java 21 REST service that manages zrok shares, frontdoors, namespaces, and related resources. Exposes a HATEOAS (HAL+JSON) API with Spring REST Docs and SpringDoc OpenAPI.

### Package Structure (`io.netfoundry.zrok`)

- **controllers/** — REST controllers. Each returns `EntityModel`/`CollectionModel` with HAL links built via `relink()` methods using `WebMvcLinkBuilder.methodOn()`.
- **domain/** — Business logic organized by aggregate: `frontdoor/`, `share/http/`, `share/tcp/`, `environment/`, `namespace/`, `healthcheck/`, `agent/`, `authprovider/`, `certificate/`, `clientcertificate/`, `customfrontend/`, `frontend/`, `location/`, `execution/`. Each subpackage typically contains: Entity, Repository, Service, and request DTOs (Create*, Update*, Find*).
- **process/** — Multi-step workflows using NetFoundry's process-framework (`@Process`, `@Step`, `@Next`, `@Metadata`). Used for long-running operations like creating/deleting TCP shares, namespaces, frontdoors, and custom frontends. Steps are executed via RabbitMQ.
- **external/** — Clients for external APIs: `zrok/` (ZrokClient), `orchestration/` (OrchestrationClient), `secrets/` (SecretManager via AWS), `certs/` (CertsClient).
- **config/** — Spring configuration classes (auth, RabbitMQ, scheduling, web).
- **validation/** — Custom Jakarta validators and validation groups (`Create`, `PutUpdate`, `PatchUpdate`).

### Key Patterns

**Repository layer**: All repositories wrap `CommonBeanRepository<Entity, IdType>` (from `common-util-framework`). They use `OrmMetadata.fromConstructor()` for mapping and Lombok `@FieldNameConstants(asEnum = true)` for type-safe column references. Queries use `FlatWhere.column()` / `FlatWhere.all()`.

**Entities**: Immutable Lombok `@Value` classes with `@Builder(toBuilder = true)` and `@Jacksonized`. Standard audit fields: `createdBy`, `createdAt`, `updatedAt`, `deletedBy`, `deletedAt`. Soft deletes via `deletedAt` column with a computed `deleted` column in MySQL.

**Shares**: Split into two separate tables/entities — `HttpShare` (in `domain/share/http/`) and `TcpShare` (in `domain/share/tcp/`). Each has its own controller (`HttpShareController`, `TcpShareController`), service, repository, and request DTOs. Shared types live in `domain/share/ShareTypes.java` (contains `ShareWrapper` builder record, `MetricsWrapper`, `DeleteShareEvent`).

**Authorization**: JWT-based via `@AuthRead`/`@AuthUpdate` annotations from `io.netfoundry.auth.clientv2`. Current user identity accessed through `AuthorizationContext.getIdentityId()`.

**IDs**: NanoId strings for domain entities (via `IdUtils`), UUIDs for frontdoor/org-level identifiers.

### Conventions

- When adding a new controller API endpoint, always create a corresponding test class in `src/test/java/io/netfoundry/zrok/controllers/` extending `BaseControllerTest`.

### Database

MySQL with Flyway migrations in `src/main/resources/db/migration/`. Schema: `zrokConnector`. UUIDs stored as `BINARY(16)`.

### Testing

- `BaseTest` → `@SpringBootTest` with `ObjectMapper`, `TestData`, mocked `CertsClient`
- `BaseControllerTest` extends `AuthenticatedApiDocTest` — provides `MockServerRestClientCustomizer` for stubbing external HTTP calls, calls `testData.init()` in `@BeforeEach`
- `TestData` is a Spring component for creating test fixtures directly in the DB
- Integration tests use Testcontainers for MySQL and Redis, mock RabbitMQ
- Test config at `src/test/resources/application.yaml` (context-path: `/zrok-connector`, scheduling disabled, in-memory processing)
