Skip to content

Commit

Permalink
Merge branch 'main' into PI-2575
Browse files Browse the repository at this point in the history
  • Loading branch information
joseph-bcl authored Oct 28, 2024
2 parents 1138917 + 80322f7 commit 3825d45
Show file tree
Hide file tree
Showing 176 changed files with 1,751 additions and 4,563 deletions.
1 change: 1 addition & 0 deletions .github/workflows/access.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ on:
- '["subject-access-requests-and-delius"]'
- '["common-platform-and-delius"]'
- '["ims-and-delius"]'
- '["appointment-reminders-and-delius"]'
# ^ add new projects here
# GitHub Actions doesn't support dynamic choices, we must add each project here to enable manual deployments
# See https://github.com/community/community/discussions/11795
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ jobs:
- subject-access-requests-and-delius
- common-platform-and-delius
- ims-and-delius
- appointment-reminders-and-delius
# ^ add new projects here
# GitHub Actions doesn't support dynamic choices, we must add each project here to enable manual deployments
# See https://github.com/community/community/discussions/11795
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ on:
- '["subject-access-requests-and-delius"]'
- '["common-platform-and-delius"]'
- '["ims-and-delius"]'
- '["appointment-reminders-and-delius"]'
# ^ add new projects here
# GitHub Actions doesn't support dynamic choices, we must add each project here to enable manual deployments
# See https://github.com/community/community/discussions/11795
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
echo >> projects/${{ matrix.project }}/.trivyignore
- name: Scan image
uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0
uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # v0.28.0
with:
image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest'
ignore-unfixed: true
Expand All @@ -56,7 +56,7 @@ jobs:
sarif_file: 'trivy-results.sarif'

- name: Get Trivy results
uses: aquasecurity/trivy-action@5681af892cd0f4997658e2bacc62bd0a894cf564 # v0.27.0
uses: aquasecurity/trivy-action@915b19bbe73b92a6cf82a1bc12b087c9a19a5fe2 # v0.28.0
with:
image-ref: 'ghcr.io/ministryofjustice/hmpps-probation-integration-services/${{ matrix.project }}:latest'
ignore-unfixed: true
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/service-catalogue.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ on:
- '["subject-access-requests-and-delius"]'
- '["common-platform-and-delius"]'
- '["ims-and-delius"]'
- '["appointment-reminders-and-delius"]'
# ^ add new projects here
# GitHub Actions doesn't support dynamic choices, we must add each project here to enable manual deployments
# See https://github.com/community/community/discussions/11795
Expand Down
12 changes: 12 additions & 0 deletions .idea/runConfigurations/appointment_reminders_and_delius.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
id("com.google.cloud.tools.jib") version "3.4.3" apply false
id("com.google.cloud.tools.jib") version "3.4.4" apply false
id("org.sonarqube") version "5.1.0.4882" apply false
`kotlin-dsl`
}
Expand All @@ -11,6 +11,6 @@ repositories {
}

dependencies {
implementation("com.google.cloud.tools:jib-gradle-plugin:3.4.3")
implementation("com.google.cloud.tools:jib-gradle-plugin:3.4.4")
implementation("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:5.1.0.4882")
}
1 change: 1 addition & 0 deletions doc/tech-docs/source/services.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,5 @@ weight: 20
* [Subject Access Requests And Delius](https://ministryofjustice.github.io/hmpps-probation-integration-services/tech-docs/projects/subject-access-requests-and-delius)
* [Common Platform And Delius](https://ministryofjustice.github.io/hmpps-probation-integration-services/tech-docs/projects/common-platform-and-delius)
* [Ims And Delius](https://ministryofjustice.github.io/hmpps-probation-integration-services/tech-docs/projects/ims-and-delius)
* [Appointment Reminders And Delius](https://ministryofjustice.github.io/hmpps-probation-integration-services/tech-docs/projects/appointment-reminders-and-delius)
<li style="display: none">^ add new projects here</li>
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.trace.SpanKind
import io.sentry.Sentry
import io.sentry.spring.jakarta.tracing.SentryTransaction
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression
import org.springframework.context.annotation.Conditional
import org.springframework.dao.CannotAcquireLockException
Expand All @@ -31,16 +32,25 @@ import java.util.concurrent.CompletionException
@ConditionalOnExpression("\${messaging.consumer.enabled:true} and '\${messaging.consumer.queue:}' != ''")
class AwsNotificationListener(
private val handler: NotificationHandler<*>,
private val objectMapper: ObjectMapper
private val objectMapper: ObjectMapper,
@Value("\${messaging.consumer.sensitive-event-types:[]}") private val sensitiveEventTypes: List<String>,
@Value("\${messaging.consumer.queue}") private val queueName: String
) {
@SentryTransaction(operation = "messaging")
@SqsListener("\${messaging.consumer.queue}")
fun receive(message: String) {
val notification = objectMapper.readValue(message, jacksonTypeRef<Notification<String>>())
notification.attributes
.extractTelemetryContext()
.withSpan(this::class.java.simpleName, "RECEIVE ${notification.eventType}", SpanKind.CONSUMER) {
Span.current().setAttribute("message", message)
.withSpan(
this::class.java.simpleName,
"RECEIVE ${notification.eventType ?: "unknown event type"}",
SpanKind.CONSUMER
) {
Span.current().setAttribute("queue", queueName)
if (notification.eventType != null && notification.eventType !in sensitiveEventTypes) {
Span.current().setAttribute("message", message)
}
try {
retry(3, RETRYABLE_EXCEPTIONS) { handler.handle(message) }
} catch (e: Throwable) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,60 +1,56 @@
package uk.gov.justice.digital.hmpps.listener

import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.ObjectMapper
import io.awspring.cloud.sqs.listener.AsyncAdapterBlockingExecutionFailedException
import io.awspring.cloud.sqs.listener.ListenerExecutionFailedException
import io.opentelemetry.api.trace.Span
import io.sentry.Sentry
import org.hamcrest.CoreMatchers.equalTo
import org.hamcrest.MatcherAssert.assertThat
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.extension.ExtendWith
import org.mockito.InjectMocks
import org.mockito.Mock
import org.mockito.Mockito.mockStatic
import org.mockito.Mockito.*
import org.mockito.junit.jupiter.MockitoExtension
import org.mockito.kotlin.any
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.springframework.messaging.support.GenericMessage
import uk.gov.justice.digital.hmpps.message.MessageAttributes
import uk.gov.justice.digital.hmpps.message.Notification
import uk.gov.justice.digital.hmpps.messaging.NotificationHandler
import uk.gov.justice.digital.hmpps.test.MockMvcExtensions.objectMapper
import java.util.concurrent.CompletionException

@ExtendWith(MockitoExtension::class)
class AwsNotificationListenerTest {
@Mock
lateinit var handler: NotificationHandler<Any>

@Mock
lateinit var objectMapper: ObjectMapper

@InjectMocks
lateinit var listener: AwsNotificationListener

@BeforeEach
fun setUp() {
whenever(objectMapper.readValue(any<String>(), any<TypeReference<Notification<String>>>()))
.thenReturn(Notification("message"))
listener = AwsNotificationListener(handler, objectMapper, listOf("my-sensitive-event-type"), "my-queue")
}

@Test
fun `messages are dispatched to handler`() {
listener.receive("message")
verify(handler).handle("message")
val notification = objectMapper.writeValueAsString(Notification("message"))
listener.receive(notification)
verify(handler).handle(notification)
}

@Test
fun `errors are captured and rethrown`() {
mockStatic(Sentry::class.java).use {
val exception = RuntimeException("error")
whenever(handler.handle("message")).thenThrow(exception)
whenever(handler.handle(any<String>())).thenThrow(exception)

assertThat(
assertThrows<RuntimeException> {
listener.receive("message")
listener.receive(objectMapper.writeValueAsString(Notification("message")))
},
equalTo(exception)
)
Expand All @@ -73,16 +69,50 @@ class AwsNotificationListenerTest {
ListenerExecutionFailedException("listener failure", meaningfulException, GenericMessage("test"))
)
)
whenever(handler.handle("message")).thenThrow(wrappedException)
whenever(handler.handle(any<String>())).thenThrow(wrappedException)

assertThat(
assertThrows<CompletionException> {
listener.receive("message")
listener.receive(objectMapper.writeValueAsString(Notification("message")))
},
equalTo(wrappedException)
)

it.verify { Sentry.captureException(meaningfulException) }
}
}

@Test
fun `sensitive messages are not logged`() {
val span = mock(Span::class.java, CALLS_REAL_METHODS)
mockStatic(Span::class.java, CALLS_REAL_METHODS).use {
it.`when`<Span> { Span.current() }.thenReturn(span)
listener.receive(
objectMapper.writeValueAsString(
Notification(
"my message",
MessageAttributes("my-sensitive-event-type")
)
)
)
verify(span, never()).setAttribute(eq("message"), any<String>())
}
}

@Test
fun `non-sensitive messages are logged`() {
val span = mock(Span::class.java, CALLS_REAL_METHODS)
mockStatic(Span::class.java, CALLS_REAL_METHODS).use {
it.`when`<Span> { Span.current() }.thenReturn(span)
listener.receive(
objectMapper.writeValueAsString(
Notification(
"my message",
MessageAttributes("some-other-event-type")
)
)
)
verify(span).setAttribute(eq("message"), any<String>())
}
}
}
Empty file.
3 changes: 3 additions & 0 deletions projects/appointment-reminders-and-delius/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# appointment-reminders-and-delius

// TODO Describe the service
62 changes: 62 additions & 0 deletions projects/appointment-reminders-and-delius/applicationinsights.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"role": {
"name": "appointment-reminders-and-delius"
},
"customDimensions": {
"service.version": "${VERSION}",
"service.team": "probation-integration"
},
"instrumentation": {
"logging": {
"level": "DEBUG"
},
"springScheduling": {
"enabled": false
}
},
"selfDiagnostics": {
"destination": "console"
},
"sampling": {
"percentage": 100
},
"preview": {
"sampling": {
"overrides": [
{
"telemetryType": "request",
"attributes": [
{
"key": "http.url",
"value": "https?://[^/]+/health/?.*",
"matchType": "regexp"
}
],
"percentage": 0
},
{
"telemetryType": "request",
"attributes": [
{
"key": "http.url",
"value": "https?://[^/]+/info/?.*",
"matchType": "regexp"
}
],
"percentage": 0
},
{
"telemetryType": "dependency",
"attributes": [
{
"key": "db.operation",
"value": "SELECT",
"matchType": "strict"
}
],
"percentage": 10
}
]
}
}
}
37 changes: 37 additions & 0 deletions projects/appointment-reminders-and-delius/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import uk.gov.justice.digital.hmpps.extensions.ClassPathExtension

apply(plugin = "com.google.cloud.tools.jib")

dependencies {
implementation(project(":libs:audit"))
implementation(project(":libs:commons"))
implementation(project(":libs:oauth-server"))

implementation("org.springframework.boot:spring-boot-starter-actuator")
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
implementation("org.springframework.boot:spring-boot-starter-security")
implementation("org.springframework.boot:spring-boot-starter-validation")
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.jetbrains.kotlin:kotlin-reflect")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-csv")
implementation(libs.springdoc)

dev(project(":libs:dev-tools"))
dev("com.h2database:h2")
dev("org.testcontainers:oracle-xe")

runtimeOnly("com.oracle.database.jdbc:ojdbc11")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation(libs.bundles.mockito)
}

configure<ClassPathExtension> {
jacocoExclusions = listOf(
"**/config/**",
"**/entity/**",
"**/AppKt.class"
)
}
13 changes: 13 additions & 0 deletions projects/appointment-reminders-and-delius/deploy/Chart.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
apiVersion: v2
appVersion: '1.0'
description: A Helm chart for Kubernetes
name: appointment-reminders-and-delius
version: 1.0.0

dependencies:
- name: generic-service
version: "3.2"
repository: https://ministryofjustice.github.io/hmpps-helm-charts
- name: generic-prometheus-alerts
version: "1.4"
repository: https://ministryofjustice.github.io/hmpps-helm-charts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
database:
access:
username_key: /appointment-reminders-and-delius/db-username
password_key: /appointment-reminders-and-delius/db-password

audit:
username: AppointmentRemindersAndDelius
forename: Appointment Reminders
surname: Service
15 changes: 15 additions & 0 deletions projects/appointment-reminders-and-delius/deploy/values-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
generic-service:
ingress:
host: appointment-reminders-and-delius-dev.hmpps.service.justice.gov.uk

scheduledDowntime:
enabled: true

env:
SENTRY_ENVIRONMENT: dev
LOGGING_LEVEL_UK_GOV_DIGITAL_JUSTICE_HMPPS: DEBUG
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_JWK_SET_URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/.well-known/jwks.json
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URI: https://sign-in-dev.hmpps.service.justice.gov.uk/auth/issuer

generic-prometheus-alerts:
businessHoursOnly: true
Loading

0 comments on commit 3825d45

Please sign in to comment.