# CLAUDE.md

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

## Commands

```bash
# Build
./install.sh              # mvn clean install with test profile

# Run (port 8314, development profile)
./run.sh                  # mvn spring-boot:run -Pdevelopment

# Build without tests
./mvnw clean install -DskipTests

# Test
./mvnw test                  # all tests
./mvnw test -Dtest=ClassName # single test class

# Code quality
./sonar.sh                # SonarQube analysis (requires SONAR_TOKEN env var)
./mvnw -Pdependency_check clean verify  # OWASP dependency check

# Docs
./docs.sh                 # REST docs + Javadoc

# Load Auth0 credentials from AWS SSM for a Terraform workspace
source auth-env.sh <workspace>
```

## Architecture

Spring Boot 3.5.8 (Java 21) microservice for NetFoundry's Customer Connect service. Currently an early-stage framework stub — business logic and domain endpoints are not yet implemented.

**Base package:** `io.netfoundry.connect`

**Key layers:**
- `config/ServerConfiguration.java` — ObjectMapper, DataSource (Hikari, max 30 connections), transactions, scheduling, RabbitMQ messaging
- `config/AuthorizationConfiguration.java` — JWT/Auth0 setup with jose4j
- `config/WebConfig.java` — HAL+JSON content negotiation, custom argument resolvers, enum converters
- `config/WhereClauseResolver.java` — Injects authorization-based SQL WHERE clauses into controller method parameters automatically

**Database**: MySQL 8 (no JPA — uses custom JDBC via `CommonBeanRepository`)

**Migrations**: Flyway (`src/main/resources/db/migration/`)

**Caching:** Redis via Redisson (distributed) + Caffeine (in-memory)

**Messaging:** RabbitMQ via Spring AMQP (prefetch: 1, concurrency: 25)

**Process framework:** NetFoundry Process Framework with Redis tracker + RabbitMQ executor

**API responses:** HAL+JSON (Spring HATEOAS), also supports JSON, YAML, Problem+JSON. All endpoints require JWT Bearer auth (Auth0).

**AWS integrations:** S3, SSM Parameter Store, Secrets Manager (SDK v2)

## Testing

Tests use Testcontainers (`DataSourceTestConfiguration.java`): MySQL 8.0.27 + Redis 6.2.6 (reusable containers). Auth0 mocked with hardcoded RSA test keys (`AuthorizationTestConfiguration.java`). S3, Secrets Manager, Parameter Store, Storage, and Messaging are mocked via `ServerTestConfiguration.java`.

Active Spring profiles: `test` or `pipeline-test`.

## Configuration Profiles

| Profile | Purpose |
|---------|---------|
| `development` | Local MySQL (port 3332), local Redis, Auth0 dev credentials |
| `test` | Testcontainers-based integration tests |
| `pipeline-test` | CI test execution |

## Architecture

### Domain-Driven Package Structure

Code is organized by domain feature under `src/main/java/io/netfoundry/core/`:

```
domain/{feature}/          # Business logic per entity
  {Entity}.java            # Immutable @Value entity (Lombok)
  Create{Entity}.java      # Creation DTO with @Builder
  Update{Entity}.java      # Update DTO
  Find{Entity}.java        # Query builder for searches
  {Entity}Service.java     # Business logic (@Service)
  {Entity}Repository.java  # JDBC data access via CommonBeanRepository
controller/                # REST controllers
external/                  # External service clients
validation/                # Custom bean validators
config/                    # Spring configuration classes
```

### Key Patterns

- **Data access**: Custom JDBC with `CommonBeanRepository<Entity, UUID>` from `common-util-framework` — not JPA/Hibernate
- **REST**: HATEOAS with HAL JSON, `EntityModel<T>` / `PagedModel<EntityModel<T>>` responses
- **Auth annotations**: `@AuthRead`, `@AuthCreate`, `@AuthDelete` on controller methods
- **Validation**: Custom constraint annotations
- **Lombok**: `@Value` for immutable entities, `@Builder` for DTOs, `@Slf4j` for logging, `@FieldNameConstants(asEnum = true)` for type-safe field references
- **Constructor injection**: Via Lombok `@RequiredArgsConstructor` (configured in `lombok.config`)

### NetFoundry Libraries (in-house)

- `common-util-framework`: JDBC utilities, REST client
- `messaging-framework`: Event publishing
- `authorization`: OAuth2 authorization

## Deployment & Infrastructure

AWS infra is managed by Terraform under `terraform/`. State lives in `s3://nf-mop-tf-state/services/customer-connect/terraform.tfstate` with DynamoDB locking. Any `terraform plan`/`apply` requires Auth0 env vars first — `source ../auth-env.sh <workspace>` (see Commands).

### Workspaces

Each workspace assumes `ops-mgmt-admin` in a different AWS account; all run in `us-east-1`:

| Workspace | Account | Subdomain |
|---|---|---|
| `nf-sandbox` | 721086286010 | sandbox.netfoundry.io |
| `nf-staging-int` | 560269805515 | staging.netfoundry.io |
| `nf-prod-int` | 745435643501 | production.netfoundry.io |

Spring profile and Spring Cloud Config label per workspace are derived from `env` map in `terraform/locals.tf`.

### What this stack owns vs. imports

**Owned:** ECS Fargate service, Cloud Map registration, IAM task role, security groups, S3 bucket (`nf-customerconnect-<env>`), MySQL password secret, Auth0 M2M client.

**Imported via `terraform_remote_state` (`terraform/imports.tf`):**
- `mop_core` → VPC, subnets, ECS cluster, RDS endpoint, Redis primary/reader endpoints, shared ALB SG, Cloud Map namespace, ES host, SNS topic, bastion IP
- `central_mq` → RabbitMQ endpoints + credentials secret

VPC, RDS, Redis, ECS cluster, and RabbitMQ are **not** provisioned by this stack.

### Runtime topology — Cloud Map, not ALB

The shared module `nf-mop-tf//modules/aws/ecs_fargate_service` does **not** attach the service to a load balancer. It registers with Cloud Map only:

- Peer services discover this one via DNS at `customer-connect-service.mop.local` (multivalue A records, 10s TTL).
- The container `healthCheck` in the task definition is the **only** traffic gate. When it first returns success, ECS marks the Cloud Map instance HEALTHY and DNS starts returning the task IP.
- The healthCheck curls `/actuator/health/readiness`, which only flips UP after Spring's `ApplicationReadyEvent`. `startPeriod: 180` suppresses startup failures but does **not** delay the first success — the first 200 response immediately marks the container HEALTHY.

If an ALB target group is ever added in the shared module, the clean split is: target group health check → readiness (gates traffic), container `healthCheck` → liveness (gates restart).

### Container image rollouts

CI builds and pushes to ECR (`745435643501.dkr.ecr.us-east-1.amazonaws.com/customer-connect`), then writes the new tag to SSM `/<env>/container-image/customer-connect-service`. Terraform reads it via `data "aws_ssm_parameter"` and interpolates it into the task definition. The `aws_ssm_parameter.container_image` resource has `lifecycle.ignore_changes = [value]` — Terraform does **not** own the tag value, CI does.

### Task definition

`terraform/templates/task-definition.json` is the container spec template. Values are interpolated from `local.task_definition_vars` in `terraform/ecs-service.tf` — RDS endpoint, Redis addresses, RabbitMQ endpoints, SNS ARN, Spring profile, Spring Cloud Config URL/label, Auth0 issuer/audience, peer service URLs, etc. Secrets (MySQL password, RabbitMQ password, Elastic metrics creds) are injected via ECS `secrets` blocks from Secrets Manager, not as plaintext env vars.

The task role and execution role are currently the same role (`customer_connect_task_role` in `terraform/iam_roles.tf`).

## Behavioral Guidelines

- Act as a senior software engineer and consultant.
- **Before you start planning or coding, ask me clarifying questions about the requirements.**
- Do not assume context. If a request is vague, ask for specifics.
- Ask questions one at a time to ensure clarity [12].
- Focus on reducing ambiguity in the prompt [2].
