This service provides access to the endpoints for the management of prisoner related activities and appointments.
The main, but not the only, client is the Activities Management UI found here.
Tools required:
- JDK v25
- Kotlin (Intellij) v2.2
- PostgresSQL v17
- Docker
- Docker Compose
Useful tools that can be installed, using Homebrew, but are not essential:
- kubectl - not essential for building the project but will be needed for other tasks.
- k9s - a terminal-based UI to interact with your Kubernetes clusters.
- jq - a lightweight and flexible command-line JSON processor.
- AWS CLI - useful if running LocalStack, interrogating queues, etc.
./gradlew
./gradlew clean build
Add a local .env file to the root of the project:
SYSTEM_CLIENT_ID=<system.client.id>
SYSTEM_CLIENT_SECRET=<system.client.secret>
DB_NAME=activities-management-db
DB_PASS=activities-management
DB_SERVER=localhost:<port>>
DB_SSL_MODE=prefer
DB_USER=activities-management
DPR_PASSWORD=dpr_password
DPR_USER=dpr_user
FEATURE_CANCEL_INSTANCE_PRIORITY_CHANGE_ENABLED=true
- You must escape any '$' characters with '\$'
SYSTEM_CLIENT_IDandSYSTEM_CLIENT_SECRETcan be extracted from the Kubernetes secrets for theDEVenvironment.DB_SERVERshould include the port of the local Postgres DB Docker container.
docker-compose up --remove-orphans
There is a script to help, which sets local profiles, port and DB connection properties to the values required.
./run-local.sh
Or, to use default port and properties
SPRING_PROFILES_ACTIVE=dev ./gradlew bootRun
You might need to set up your AWS config and credentials files:
Sample config file content
[profile default]
region = eu-west-2
Sample credentials file content
[default]
aws_access_key_id = foo
aws_secret_access_key = bar
There are some example scripts to simulate publishing and consuming messages in the util_scripts/localstack folder.
./gradlew test
./gradlew integrationTest
To list project dependencies, run:
./gradlew dependencies
To check for dependency updates, run:
./gradlew dependencyUpdates --warning-mode all
To run an OWASP dependency check, run:
./gradlew clean dependencyCheckAnalyze --info
To upgrade the gradle wrapper version, run:
./gradlew wrapper --gradle-version=<VERSION>
To automatically update project dependencies, run:
./gradlew useLatestVersions
To run Ktlint check:
./gradlew ktlintCheck
To run Ktlint format:
./gradlew ktlintFormat
To apply ktlint styles to intellij
./gradlew ktlintApplyToIdea
To register pre-commit check to run Ktlint format:
./gradlew ktlintApplyToIdea addKtlintFormatGitPreCommitHook
...or to register pre-commit check to only run Ktlint check:
./gradlew ktlintApplyToIdea addKtlintCheckGitPreCommitHook
The service uses Sentry.IO to raise alerts in Slack and email for job failures. There is a project and team set up in Sentry specifically for this service called #hmpps-activities-management. You can log in (and register if need be) with your MoJ github account here.
Rules for alerts can be configured here.
For Sentry integration to work it requires the environment variable SENTRY_DSN to be set up in Kubernettes. This value for this can be found here.
echo -n '<SENTRY_DSN_GOES_HERE>' | base64
Create a file called sentry.yaml based on the example below and add the base 64 encoded to it:
apiVersion: v1
kind: Secret
metadata:
name: sentry
type: Opaque
data:
SENTRY_DSN: <BASE_64_ENCODED_SENTRY_DSN_GOES_HERE>
Apply the secret to each environment with kubectl using the file above as required:
kubectl -n hmpps-activities-management-<dev|preprod|prod> apply -f sentry.yaml
This service is dependent on the following services:
- Auth API - authorisation and authentication
- Activities Management API - activities management api
- Case Notes API - case notes api
- Incentives API - incentives api
- Locations Inside Prison API - locations inside prison api
- Manage Adjudications API - manage adjudications api
- Nomis Mapping API - nomis mapping api
- Non-Associations API - non-associations api
- Prison API - prison api
- Prisoner Search API - prisoner search api
IMPORTANT: extreme caution should be taken if the job failure in question is in production and probably warrants two developers working together.
There may be times on overnight job fails to run. To re-run it you can port forward onto a running pod and make a curl request to the job in question.
If for example the attendance creation failed it can be re-run as follows:
In a terminal window port forward to one of the API pods.
kubectl -n hmpps-activities-management-<dev|preprod|prod> get pods | grep apiUsing one of the API pods listed from the command above do the following
kubectl -n hmpps-activities-management-<dev|preprod|prod> port-forward hmpps-activities-management-api-<POD_SUFFIX_GOES_HERE> 8080:8080
In another terminal window a curl command to run the job (the one below is running attendance creation)
curl -XPOST "http://localhost:8080/job/manage-attendance-records?date=2023-11-18&prisonCode=XYZ"IMPORTANT: As a rule of thumb, once people are out of the prison in question, we don't want to publish events for them. If in doubt, check with the team responsible for the sync service.
There may be times we need to manually raise an event in production e.g. on the back of a bug fix.
In a terminal window port forward to one of the API pods
kubectl -n hmpps-activities-management-<dev|preprod|prod> get pods | grep apiUsing one of the API pods listed from the command above do the following
kubectl -n hmpps-activities-management-<dev|preprod|prod> port-forward hmpps-activities-management-api-<POD_SUFFIX_GOES_HERE> 8080:8080
In another terminal window issue a curl command to publish the event e.g. PRISONER_ALLOCATION_AMENDED
curl --location 'http://localhost:8080/utility/publish-events' \
--header 'Content-Type: application/json' \
--data '{
"outboundEvent":"<EVENT_TYPE_GOES_HERE>",
"identifiers": [
1
]
}'
The logs are written to Application Insights and can be queried accordingly. There is roughly a 5 minute delay before you can see them.
Some simple example Application Insights queries:
traces
| where cloud_RoleName == 'hmpps-activities-management-api'
traces
| where cloud_RoleName == 'hmpps-activities-management-api'
| where message has 'ABC'
union *
| where operation_Id ==‘<INSERT OPERATION ID HERE>’
|project timestamp, message, itemType, name, operation_Name, cloud_RoleName, resultCode, customDimensions, duration, url, data, innermostMessage
|sort by timestamp asc
The Dockerfile relies on the application being built first. Steps to build the docker image:
- Build the jar files
./gradlew clean assemble- Copy the jar files to the base directory so that the docker build can find them
cp build/libs/*.jar .- Build the docker image with required arguments
docker build -t activities-api .- Run the docker image
./run-docker-local.sh