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
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
SERVER_PORT=4550
BASE_IMAGE=openjdk:21-jdk-slim
JAR_FILENAME=service-hmcts-marketplace-springboot-template-*.jar
JAR_FILENAME=cp-case-document-knowledge-service-*.jar
JAR_FILE_PATH=build/libs
36 changes: 0 additions & 36 deletions Dockerfile

This file was deleted.

266 changes: 195 additions & 71 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,116 +1,240 @@
# HMCTS Service Spring Boot Template
# case-document-knowledge-service

This repository provides a template for building Spring Boot applications. While the initial use case was for the HMCTS API Marketplace, the template is designed to be reusable across jurisdictions and is intended as a base paved path for wider adoption.
**AI-powered answers for case documents — every response cited and auditable.**
Spring Boot 4 (Java 21) REST API with OpenAPI docs, PostgreSQL + Flyway, production-ready observability, and CI-friendly **Gradle** build.

It includes essential configurations, dependencies, and recommended practices to help teams get started quickly.
---

**Note:** This template is not a framework, nor is it intended to evolve into one. It simply leverages the Spring ecosystem and proven libraries from the wider engineering community.
## Table of contents

As HMCTS services are hosted on Azure, the included dependencies reflect this. Our aim is to stay as close to the cloud as possible in order to maximise alignment with the Shared Responsibility Model and achieve optimal security and operability.
- [Features](#features)
- [Tech stack](#tech-stack)
- [Project layout](#project-layout)
- [Prerequisites](#prerequisites)
- [Build & test (Gradle)](#build--test-gradle)
- [Run locally (Gradle)](#run-locally-gradle)
- [Run with Docker Compose](#run-with-docker-compose)
- [Integration tests with Docker Compose](#integration-tests-with-docker-compose)
- [Configuration](#configuration)
- [API & docs](#api--docs)
- [Observability](#observability)
- [SBOM / dependency insights](#sbom--dependency-insights)
- [Troubleshooting](#troubleshooting)
- [License](#license)

## Want to Build Your Own Path?
---

That’s absolutely fine — but if you do, make sure your approach meets the following baseline requirements:
## Features

* Security – All services must meet HMCTS security standards, including vulnerability scanning and least privilege access.
* Observability – Logs, metrics, and traces must be integrated into HMCTS observability stack (e.g. Azure Monitoring).
* Audit – Systems must produce audit trails that meet legal and operational requirements.
* CI/CD Integration – Pipelines must include automated testing, deployments to multiple environments, and use approved tooling (e.g. GitHub Actions or Azure DevOps).
* Compliance & Policy Alignment – Services must align with HMCTS/MoJ policies (e.g. Coding in the Open, mandatory security practices).
* Ownership & Support – Domain teams must clearly own the service, maintain a support model, and define escalation paths.
- Versioned **REST API** under `/api/v1/**`
- **OpenAPI / Swagger UI**
- **PostgreSQL** persistence with **Flyway** migrations
- **Observability**: Actuator health, Prometheus metrics, OTLP tracing, structured JSON logs
- **Quality gates**: PMD, JaCoCo coverage
- **Gradle 9** build with Docker-Compose-backed **integration tests**

## Installation
> Note: HTTP security/authorization is intentionally minimal by default. Integrate with your gateway or add your own authorization as needed.

To get started with this project, you'll need Java and Gradle installed.
---

### Prerequisites
## Tech stack

- ☕️ **Java 21 or later**: Ensure Java is installed and available on your `PATH`.
- ⚙️ **Gradle**: You can install Gradle using your preferred method:
- **Java 21**, **Spring Boot 4.0.0-M2**
- Spring MVC, Spring Data JPA, **PostgreSQL 16**, Flyway
- springdoc-openapi 3.x (Swagger UI)
- Micrometer + **Prometheus**, OpenTelemetry OTLP exporter
- Gradle 9, PMD, JaCoCo
- Docker / Docker Compose v2

**macOS (Recommended with Homebrew):**
```bash
brew install gradle
```
---

**Other Platforms:**
Visit the [Gradle installation guide](https://gradle.org/install/) for platform-specific instructions.
## Project layout

```
.
├─ src/main/java/... # application code
├─ src/main/resources/
│ ├─ application.yml # prod-ready defaults
│ └─ db/migration/ # Flyway migrations
├─ src/test/java/... # unit tests
├─ src/integrationTest/java/... # integration tests (Gradle sourceSet)
├─ docker/
│ ├─ Dockerfile
│ └─ docker-compose.integration.yml
└─ build.gradle # Gradle build
```

---

## Prerequisites

- Java **21**
- Docker Engine **v27+** and **Docker Compose v2** (`docker compose` CLI)
- Nothing else required — use the Gradle wrapper (`./gradlew`)

---

## Build & test (Gradle)

```bash
# Clean & full build (unit tests)
./gradlew clean build

# Faster local build (skip tests)
./gradlew -x test clean build

# Run only unit tests
./gradlew test

# Run only integration tests
./gradlew integration
# Code quality reports
./gradlew pmdMain pmdTest jacocoTestReport
# Open reports
open build/reports/pmd/main.html
open build/reports/tests/test/index.html
open build/reports/jacoco/test/html/index.html

# Dependency insight (useful for conflicts)
./gradlew dependencyInsight --dependency <group-or-module>
```

---

## Run locally (Gradle)

By default the app starts on **:8082** and looks for Postgres at **localhost:55432** (see [Configuration](#configuration)).

```bash
# Start with your local Java 21
./gradlew bootRun
```

---

## Run with Docker Compose

This brings up **PostgreSQL 16** (exposed as `localhost:55432`) and the **app** (exposed as `localhost:8082`).
It uses `docker/docker-compose.integration.yml`.

You can verify installation with:
```bash
java -version
gradle -v
# Build image & start stack
docker compose -f docker/docker-compose.integration.yml up -d --build

# Tail logs
docker compose -f docker/docker-compose.integration.yml logs -f app

# Stop & remove (keep volumes)
docker compose -f docker/docker-compose.integration.yml down

# Remove everything inc. volumes (⚠️ deletes DB data)
docker compose -f docker/docker-compose.integration.yml down -v
```

---

## Integration tests with Docker Compose

Integration tests automatically **bring up** Postgres + app, **wait** until they’re healthy, run tests, then **tear down** the stack.

```bash
./gradlew clean integration
```

#### Add Gradle Wrapper
What happens under the hood:

run `gradle wrapper`
- Gradle task `integration` depends on `composeUp` (from the Compose plugin)
- `composeUp` builds the app image, starts Postgres (host port **55432**) and app (**8082**), and waits for health
- Tests run against `http://localhost:8082`
- `composeDown` is always called to clean up

### Environment Setup for Local Builds
---

Recommended Approach for macOS Users (using `direnv`)
## Configuration

If you're on macOS, you can use [direnv](https://direnv.net/) to automatically load these environment variables per project:
The application is configured via environment variables with sensible defaults. Key settings:

1. Install `direnv`:
```bash
brew install direnv
```
| Property | Default | Notes |
|----------------------------------|----------------------------------------------|-------------------------------------|
| `server.port` | `8082` | API & Actuator port |
| `SPRING_DATASOURCE_URL` | `jdbc:postgresql://localhost:55432/appdb` | Postgres JDBC URL |
| `SPRING_DATASOURCE_USERNAME` | `app` | DB user |
| `SPRING_DATASOURCE_PASSWORD` | `app` | DB password |
| `DB_POOL_SIZE` | `10` | Hikari pool size |
| `TRACING_SAMPLER_PROBABILITY` | `1.0` | OTel tracing sample rate |
| `OTEL_TRACES_URL` | `http://localhost:4318/v1/traces` | OTLP traces endpoint |
| `OTEL_METRICS_ENABLED` | `false` | Export metrics via OTLP if `true` |
| `OTEL_METRICS_URL` | `http://localhost:4318/v1/metrics` | OTLP metrics endpoint |

2. Hook it into your shell (example for bash or zsh):
```bash
echo 'eval "$(direnv hook bash)"' >> ~/.bash_profile
# or for zsh
echo 'eval "$(direnv hook zsh)"' >> ~/.zshrc
```
Flyway is enabled and points at `classpath:db/migration`.

4. Allow `direnv` to load:
```bash
direnv allow
```
---

This will ensure your environment is correctly set up every time you enter the project directory.
## API & docs

## Static code analysis
- Base URL: `http://localhost:8082`
- Swagger UI: `http://localhost:8082/swagger-ui.html`
(OpenAPI JSON at `/v3/api-docs`)

Install PMD
Example smoke:

```bash
brew install pmd
curl -fsS "http://localhost:8082/actuator/health" | jq
```

---

## Observability

Actuator endpoints (same port as API):

| Endpoint | Purpose |
|-----------------------------|---------------------------------|
| `/actuator/health` | Overall health (UP/DOWN) |
| `/actuator/health/liveness` | Liveness probe |
| `/actuator/health/readiness`| Readiness probe |
| `/actuator/info` | App/build info (if configured) |
| `/actuator/prometheus` | Prometheus/OpenMetrics scrape |

The service logs JSON to STDOUT (Logback + logstash-encoder).
OTel tracing is pre-wired; set the `OTEL_*` env vars above to export.

Quick checks:

```bash
pmd check \
--dir src/main/java \
--rulesets \
.github/pmd-ruleset.xml \
--format html \
-r build/reports/pmd/pmd-report.html
curl -fsS http://localhost:8082/actuator/health
curl -fsS http://localhost:8082/actuator/prometheus | head
```

## Pact Provider Test
---

## SBOM / dependency insights

Run pact provider test and publish verification report to pact broker locally
Generate a CycloneDX SBOM:

Update .env file with below details (replacing placeholders with actual values):
```bash
export PACT_PROVIDER_VERSION="0.1.0-local-YOUR-INITIALS" # or any version you want to use
export PACT_VERIFIER_PUBLISH_RESULTS=true
export PACT_PROVIDER_BRANCH="ANY_BRANCH_NAME_THAT_IS_NOT_A_DEFAULT_ONE"
export PACT_BROKER_TOKEN="YOUR_PACTFLOW_BROKER_TOKEN"
export PACT_BROKER_URL="https://hmcts-dts.pactflow.io"
export PACT_ENV="local" # or value based on the environment you are testing against
./gradlew cyclonedxBom
# Output at: build/reports/bom.json (or .xml depending on configuration)
```
Run Pact tests:

Print dependency trees:

```bash
gradle pactVerificationTest
./gradlew -q dependencies --configuration runtimeClasspath
```

### Contribute to This Repository
---

## Troubleshooting

Contributions are welcome! Please see the [CONTRIBUTING.md](.github/CONTRIBUTING.md) file for guidelines.
- **Port in use** — Change ports in `application.yml` or Compose file. DB uses **55432** to avoid conflicts with a local 5432.
- **Compose not found** — You need **Docker Compose v2** (`docker compose`). The Gradle plugin calls the Docker CLI directly.
- **DB auth failures** — Ensure env vars match Postgres service in Compose: `app/app@appdb`.
- **Slow startup in CI** — Increase Compose wait timeouts (plugin `upAdditionalArgs`) if needed.
- **Gradle “buildDir deprecated”** — The build uses `layout.buildDirectory`; avoid legacy `buildDir` in custom tasks.

See also: [JWTFilter documentation](docs/JWTFilter.md)
---

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
MIT
8 changes: 4 additions & 4 deletions bin/run-in-docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ GRADLE_INSTALL=false

# TODO custom environment variables application requires.
# TODO also consider enlisting them in help string above ^
# TODO sample: DB_PASSWORD Defaults to 'dev'
# TODO sample: CP_CDK_DB_PASSWORD Defaults to 'dev'
# environment variables
#DB_PASSWORD=dev
#CP_CDK_DB_PASSWORD=dev
#S2S_URL=localhost
#S2S_SECRET=secret

Expand All @@ -47,7 +47,7 @@ execute_script() {

# echo "Assigning environment variables.."
#
# export DB_PASSWORD=${DB_PASSWORD}
# export CP_CDK_DB_PASSWORD=${CP_CDK_DB_PASSWORD}
# export S2S_URL=${S2S_URL}
# export S2S_SECRET=${S2S_SECRET}

Expand All @@ -63,7 +63,7 @@ while true ; do
-i|--install) GRADLE_INSTALL=true ; shift ;;
-p|--param)
case "$2" in
# DB_PASSWORD=*) DB_PASSWORD="${2#*=}" ; shift 2 ;;
# CP_CDK_DB_PASSWORD=*) CP_CDK_DB_PASSWORD="${2#*=}" ; shift 2 ;;
# S2S_URL=*) S2S_URL="${2#*=}" ; shift 2 ;;
# S2S_SECRET=*) S2S_SECRET="${2#*=}" ; shift 2 ;;
*) shift 2 ;;
Expand Down
Loading
Loading