diff --git a/services/ehr-transfer-service/Dockerfile b/services/ehr-transfer-service/Dockerfile index 024947f7..104bd497 100644 --- a/services/ehr-transfer-service/Dockerfile +++ b/services/ehr-transfer-service/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:21-jre-alpine-3.23 +FROM eclipse-temurin:25.0.0_36-jre-alpine-3.23 RUN apk update && \ apk -u list && \ diff --git a/services/ehr-transfer-service/Makefile b/services/ehr-transfer-service/Makefile deleted file mode 100644 index 5b40766d..00000000 --- a/services/ehr-transfer-service/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -test-environment: - ACTIVATE_PRO=0 localstack start -d - ./tasks test_all \ No newline at end of file diff --git a/services/ehr-transfer-service/README.md b/services/ehr-transfer-service/README.md index 7236dfb4..405f5d7d 100644 --- a/services/ehr-transfer-service/README.md +++ b/services/ehr-transfer-service/README.md @@ -1,15 +1,15 @@ ### prm-repo-ehr-transfer-service -This component of the Repository is reponsible for the request and receiving (incoming transfer) of a patient's -Electronic Health Record over GP2GP. +This component of Orphaned Record Continuity (ORC) is responsible for the requesting and receiving (incoming transfer) +of a patient's Electronic Health Record over GP2GP. -It is the first receiver of all inbound messages (from MHS inbound) for the Repository and will send on +It is the first receiver of all inbound messages (from MHS inbound) for the ORC and will send on irrelevant messages to a downstream "not handled" queue so that they can be actioned by the service responsible. ## Prerequisites -- Java 21 LTS -- Gradle 8.5 +- Java 25 LTS +- Gradle 9.3 ### AWS helpers @@ -31,48 +31,35 @@ You can access the queues using the Active MQ console on: `http://localhost:8161 ### Running the tests -Run the unit tests with: -`./tasks test_unit` +#### All tests +To run all Unit and Integration tests and produce a coverage report, in your terminal, run `./tasks test_all` -In your terminal with -`./gradlew test` +#### Unit testing +These are easiest to run from your IDE, however, you can also run them from your terminal with: `./tasks test_unit` -Run the integration tests with: +#### Integration testing +The integration tests can be run from your terminal with: `./tasks test_integration` which will start and stop LocalStack for you. -`./tasks test_integration` - -Alternatively, you can use `play` button next to each test in IntelliJ (your IDE) - -Run the coverage tests with: - -`./tasks test_coverage` - -Run the dependency check tests with: - -`./tasks dep` - -To run all the checks before committing with: - -`./tasks test_all` +If you want to run these from your IDE, you must first start LocalStack with: `./tasks start_localstack` +It is recommended that you stop LocalStack after running the tests with: `./tasks stop_localstack` ### Config If you need to add any new configuration items, update the `src/main/resources/application.properties` file per -environment as well as add the environment variables in `./tasks` `configure_local_envariables`. Note that `test` -directory has its own `application.properties` file used in the test suite. +environment as well as add the environment variables in `./tasks` `configure_local_envariables`. -Ensure you have VPN connection set up to both `dev` and `test` environments: +Ensure you have VPN connection set up to `dev` environment: [CLICK HERE](https://gpitbjss.atlassian.net/wiki/spaces/TW/pages/1832779966/VPN+for+Deductions+Services) ### Setup -In AmazonMQ settings for either the `dev` or `test` provision. Edit the `deductor-amq-broker-${NHS_ENVIRONMENT}` +In AmazonMQ settings for the `dev` provision. Edit the `deductor-amq-broker-${NHS_ENVIRONMENT}` security group inbound rules. Add new rule that allows All TCP from the `${NHS_ENVIRONMENT} VPN VM security group`, apply before running the following: ``` // Starts the server locally using `.env` -$ NHS_ENVIRONMENT=test +$ NHS_ENVIRONMENT=dev ``` ## Access to AWS diff --git a/services/ehr-transfer-service/build.gradle b/services/ehr-transfer-service/build.gradle index ca00bbca..b2945da2 100644 --- a/services/ehr-transfer-service/build.gradle +++ b/services/ehr-transfer-service/build.gradle @@ -1,39 +1,40 @@ plugins { - id 'org.springframework.boot' version '3.2.1' - id 'io.spring.dependency-management' version '1.1.4' + id 'org.springframework.boot' version '4.0.2' + id 'io.spring.dependency-management' version '1.1.7' id 'java' id 'jacoco' - id 'com.github.spotbugs' version '6.0.6' - id 'org.sonarqube' version '4.3.1.3277' + id 'com.github.spotbugs' version '6.4.8' + id 'org.sonarqube' version '7.2.2.6593' } group = 'uk.nhs.prm.repo' version = '0.0.1-SNAPSHOT' -configurations { - compileOnly { - extendsFrom annotationProcessor - } - compile.exclude module: 'activemq-broker' -} - repositories { mavenCentral() } java { toolchain { - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(25) } } -//Without this task two jars are built, the additional "-plain.jar" is not needed -// for more details refer to: https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#packaging-executable.and-plain-archives -jar { - enabled = false +sourceSets { + integration { + compileClasspath += sourceSets.main.output + runtimeClasspath += sourceSets.main.output + compileClasspath += sourceSets.test.output + runtimeClasspath += sourceSets.test.output + } } configurations { + compileOnly { + extendsFrom annotationProcessor + } + implementation.exclude module: 'activemq-broker' + integrationImplementation.extendsFrom testImplementation integrationRuntimeClasspath.extendsFrom testRuntimeClasspath } @@ -42,74 +43,61 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-activemq' testImplementation 'org.springframework.boot:spring-boot-starter-test' - implementation('io.netty:netty-buffer') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec-http') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec-http2') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-common') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-handler') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-resolver') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport-classes-epoll') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport-native-unix-common') { version { strictly '4.1.104.Final' } } - - implementation 'org.yaml:snakeyaml:2.2' - implementation 'net.logstash.logback:logstash-logback-encoder:7.0.1' - implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.14.2' - implementation 'com.sun.mail:javax.mail:1.6.2' - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'com.github.spotbugs:spotbugs-annotations:4.8.3' - implementation 'junit:junit:4.13.2' - implementation 'jakarta.xml.bind:jakarta.xml.bind-api:3.0.1' + implementation('io.netty:netty-buffer') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec-http') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec-http2') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-common') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-handler') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-resolver') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport-classes-epoll') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport-native-unix-common') { version { strictly '4.2.9.Final' } } + + implementation 'org.yaml:snakeyaml:2.5' + implementation 'net.logstash.logback:logstash-logback-encoder:9.0' + implementation 'com.sun.mail:jakarta.mail:2.0.2' + implementation 'com.google.code.gson:gson:2.13.2' + implementation 'com.github.spotbugs:spotbugs-annotations:4.9.8' + implementation 'jakarta.xml.bind:jakarta.xml.bind-api:4.0.4' + // TODO PRM-666 This is the latest version from March 2023 - Find replacement? implementation 'org.apache.qpid:proton-j:0.34.1' - implementation platform('software.amazon.awssdk:bom:2.20.130') + implementation platform('tools.jackson:jackson-bom:3.0.4') + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml' + + implementation platform('software.amazon.awssdk:bom:2.41.14') implementation 'software.amazon.awssdk:cloudwatch' implementation 'software.amazon.awssdk:sns' implementation 'software.amazon.awssdk:sqs' implementation 'software.amazon.awssdk:s3' implementation 'software.amazon.awssdk:dynamodb' - implementation 'com.amazonaws:amazon-sqs-java-extended-client-lib:1.2.2' - implementation 'software.amazon.sns:sns-extended-client:1.1.3' - implementation 'software.amazon.payloadoffloading:payloadoffloading-common:1.1.1' - - // Once we upgrade amazon-sqs-java-messaging-lib to v2 we can remove aws-java-sdk-core and sqs. - implementation 'com.amazonaws:amazon-sqs-java-messaging-lib:1.1.2' - implementation 'com.amazonaws:aws-java-sdk-core:1.12.368' - implementation 'com.amazonaws:aws-java-sdk-sqs:1.12.533' + implementation 'com.amazonaws:amazon-sqs-java-extended-client-lib:2.1.2' + implementation 'com.amazonaws:amazon-sqs-java-messaging-lib:2.1.4' + implementation 'software.amazon.payloadoffloading:payloadoffloading-common:2.1.0' - compileOnly 'org.projectlombok:lombok:1.18.30' + compileOnly 'org.projectlombok:lombok:1.18.42' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'org.hamcrest:hamcrest:2.2' - testImplementation(platform('org.junit:junit-bom:5.10.1')) + testImplementation 'org.hamcrest:hamcrest:3.0' + testImplementation(platform('org.junit:junit-bom:6.0.2')) testImplementation('org.junit.jupiter:junit-jupiter') - spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.11.0' - testImplementation 'commons-fileupload:commons-fileupload:1.5' - testImplementation 'org.wiremock:wiremock-standalone:3.3.1' - testImplementation 'com.google.guava:guava:33.0.0-jre' - testImplementation 'org.awaitility:awaitility:4.2.0' + spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.14.0' + testImplementation 'commons-fileupload:commons-fileupload:1.6.0' + testImplementation 'org.wiremock:wiremock-standalone:3.13.2' + testImplementation 'com.google.guava:guava:33.5.0-jre' + testImplementation 'org.awaitility:awaitility:4.3.0' - integrationImplementation 'com.swiftmq:swiftmq-client:13.1.2' + integrationImplementation 'com.swiftmq:swiftmq-client:13.2.0' } test { useJUnitPlatform() + outputs.upToDateWhen { false } } -test.outputs.upToDateWhen { false } - -sourceSets { - integration { - compileClasspath += sourceSets.main.output - runtimeClasspath += sourceSets.main.output - compileClasspath += sourceSets.test.output - runtimeClasspath += sourceSets.test.output - } -} - -tasks.withType(Test) { +tasks.withType(Test).configureEach { useJUnitPlatform() } @@ -117,11 +105,7 @@ task integration(type: Test) { testClassesDirs = sourceSets.integration.output.classesDirs classpath = sourceSets.integration.runtimeClasspath mustRunAfter tasks.test -} - -integration.outputs.upToDateWhen { false } - -integration { + outputs.upToDateWhen { false } systemProperty "javax.xml.parsers.SAXParserFactory", "com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl" } @@ -138,8 +122,8 @@ spotbugsMain { ignoreFailures = true reports { html { - enabled = true - destination = file("$buildDir/reports/spotbugs/main/spotbugs.html") + required = true + outputLocation = file("$buildDir/reports/spotbugs/main/spotbugs.html") stylesheet = 'fancy-hist.xsl' } } @@ -149,8 +133,8 @@ spotbugsTest { ignoreFailures = true reports { html { - enabled = true - destination = file("$buildDir/reports/spotbugs/test/spotbugs.html") + required = true + outputLocation = file("$buildDir/reports/spotbugs/test/spotbugs.html") stylesheet = 'fancy-hist.xsl' } } @@ -160,8 +144,8 @@ spotbugsIntegration { ignoreFailures = true reports { html { - enabled = true - destination = file("$buildDir/reports/spotbugs/integration/spotbugs.html") + required = true + outputLocation = file("$buildDir/reports/spotbugs/integration/spotbugs.html") stylesheet = 'fancy-hist.xsl' } } @@ -174,5 +158,3 @@ sonar { property 'sonar.host.url', 'https://sonarcloud.io' } } - -check.dependsOn integration diff --git a/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.jar b/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.jar index 62d4c053..61285a65 100644 Binary files a/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.jar and b/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.jar differ diff --git a/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.properties b/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.properties index a5952066..19a6bdeb 100644 --- a/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.properties +++ b/services/ehr-transfer-service/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/services/ehr-transfer-service/gradlew b/services/ehr-transfer-service/gradlew index fbd7c515..adff685a 100755 --- a/services/ehr-transfer-service/gradlew +++ b/services/ehr-transfer-service/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,81 +15,114 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,88 +131,118 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/services/ehr-transfer-service/gradlew.bat b/services/ehr-transfer-service/gradlew.bat new file mode 100644 index 00000000..c4bdd3ab --- /dev/null +++ b/services/ehr-transfer-service/gradlew.bat @@ -0,0 +1,93 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/configuration/LocalStackAwsConfig.java b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/configuration/LocalStackAwsConfig.java index 86d8e937..f3532f63 100644 --- a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/configuration/LocalStackAwsConfig.java +++ b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/configuration/LocalStackAwsConfig.java @@ -1,22 +1,10 @@ package uk.nhs.prm.repo.ehrtransferservice.configuration; -import com.amazon.sqs.javamessaging.AmazonSQSExtendedClient; -import com.amazon.sqs.javamessaging.ExtendedClientConfiguration; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; -import com.amazonaws.services.sns.AmazonSNS; -import com.amazonaws.services.sns.AmazonSNSClientBuilder; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; import jakarta.annotation.PostConstruct; import jakarta.jms.ConnectionFactory; import org.apache.activemq.ActiveMQConnectionFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; @@ -37,16 +25,15 @@ import software.amazon.awssdk.services.sns.model.CreateTopicResponse; import software.amazon.awssdk.services.sns.model.SubscribeRequest; import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.payloadoffloading.S3BackedPayloadStore; -import software.amazon.payloadoffloading.S3Dao; -import software.amazon.sns.AmazonSNSExtendedClient; -import software.amazon.sns.SNSExtendedClientConfiguration; +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; import java.net.URI; import java.net.URISyntaxException; import java.util.*; -import static javax.jms.Session.CLIENT_ACKNOWLEDGE; +import static jakarta.jms.Session.CLIENT_ACKNOWLEDGE; import static uk.nhs.prm.repo.ehrtransferservice.database.enumeration.TransferTableAttribute.*; @TestConfiguration @@ -56,7 +43,7 @@ public class LocalStackAwsConfig { private S3Client s3Client; @Autowired - private AmazonSQSAsync amazonSQSAsync; + private SqsClient sqsClient; @Autowired private DynamoDbClient dynamoDbClient; @@ -123,21 +110,24 @@ public class LocalStackAwsConfig { private static final long DYNAMO_WRITE_CAPACITY_UNITS = 5L; @Bean - public static SqsClient sqsClient(@Value("${localstack.url}") String localstackUrl) throws URISyntaxException { + public static SqsClient sqsClient( + @Value("${localstack.url}") String localstackUrl, + @Value("${aws.region}") String region + ) throws URISyntaxException { return SqsClient.builder() - .credentialsProvider((()-> AwsBasicCredentials.create("LSIAQAAAAAAVNCBMPNSG", "LSIAQAAAAAAVNCBMPNSG"))) + .credentialsProvider(() -> AwsBasicCredentials.create("LSIAQAAAAAAVNCBMPNSG", "LSIAQAAAAAAVNCBMPNSG")) .endpointOverride(new URI(localstackUrl)) + .region(Region.of(region)) .build(); } @Bean - public JmsListenerContainerFactory myFactory(ConnectionFactory connectionFactory, - DefaultJmsListenerContainerFactoryConfigurer configurer) { + public JmsListenerContainerFactory myFactory( + ConnectionFactory connectionFactory + ) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); factory.setSessionAcknowledgeMode(CLIENT_ACKNOWLEDGE); - // This provides all boot's default to this factory, including the message converter - configurer.configure(factory, connectionFactory); - // You could still override some of Boot's default if necessary. + factory.setConnectionFactory(connectionFactory); return factory; } @@ -151,22 +141,13 @@ public ConnectionFactory connectionFactory() { } @Bean - public AmazonS3 amazonS3(@Value("${localstack.url}") String localstackUrl) { - return AmazonS3ClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("LSIAQAAAAAAVNCBMPNSG", "LSIAQAAAAAAVNCBMPNSG"))) - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(localstackUrl, "eu-west-2")) - .build(); - } - - // TODO: this S3Client bean is used to setup large message bucket only. - // the real dependency used in code is the one above (AmazonS3 / v1). - // Therefore: find a way to create the bucket - setting GrantFullControl using - // the class above, then get rid of this S3Client / v2. - @Bean - public static S3Client s3Client(@Value("${localstack.url}") String localstackUrl) { + public static S3Client s3Client( + @Value("${localstack.url}") String localstackUrl, + @Value("${aws.region}") String region + ) { return S3Client.builder() .endpointOverride(URI.create(localstackUrl)) - .region(Region.EU_WEST_2) + .region(Region.of(region)) .credentialsProvider(StaticCredentialsProvider.create(new AwsCredentials() { @Override public String accessKeyId() { @@ -190,36 +171,13 @@ private String failoverUrl() { } @Bean - public static AmazonSQSAsync amazonSQSAsync(@Value("${localstack.url}") String localstackUrl) { - return AmazonSQSAsyncClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("LSIAQAAAAAAVNCBMPNSG", "LSIAQAAAAAAVNCBMPNSG"))) - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(localstackUrl, "eu-west-2")) - .build(); - } - - @Bean - public static AmazonSQSExtendedClient s3SupportedSqsClient(AmazonSQSAsync sqsClient, AmazonS3 amazonS3, @Value("${aws.sqsLargeMessageBucketName}") String sqsLargeMessageBucketName) { - return new AmazonSQSExtendedClient(sqsClient, new ExtendedClientConfiguration().withPayloadSupportEnabled(amazonS3, sqsLargeMessageBucketName, true)); - } - - @Bean - public static AmazonSNS amazonSNS(@Value("${localstack.url}") String localstackUrl) { - return AmazonSNSClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("LSIAQAAAAAAVNCBMPNSG", "LSIAQAAAAAAVNCBMPNSG"))) - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(localstackUrl, "eu-west-2")) - .build(); - } - - @Bean - public static AmazonSNSExtendedClient s3SupportedSnsClient(AmazonSNS amazonSNS, AmazonS3 amazonS3, @Value("${aws.sqsLargeMessageBucketName}") String sqsLargeMessageBucketName) { - return new AmazonSNSExtendedClient(amazonSNS, new SNSExtendedClientConfiguration(), new S3BackedPayloadStore(new S3Dao(amazonS3), sqsLargeMessageBucketName)); - } - - @Bean - public static SnsClient snsClient(@Value("${localstack.url}") String localstackUrl) { + public static SnsClient snsClient( + @Value("${localstack.url}") String localstackUrl, + @Value("${aws.region}") String region + ) { return SnsClient.builder() .endpointOverride(URI.create(localstackUrl)) - .region(Region.EU_WEST_2) + .region(Region.of(region)) .credentialsProvider(StaticCredentialsProvider.create(new AwsCredentials() { @Override public String accessKeyId() { @@ -235,10 +193,13 @@ public String secretAccessKey() { } @Bean - public static DynamoDbClient dynamoDbClient(@Value("${localstack.url}") String localstackUrl) { + public static DynamoDbClient dynamoDbClient( + @Value("${localstack.url}") String localstackUrl, + @Value("${aws.region}") String region + ) { return DynamoDbClient.builder() .endpointOverride(URI.create(localstackUrl)) - .region(Region.EU_WEST_2) + .region(Region.of(region)) .credentialsProvider( StaticCredentialsProvider.create(new AwsCredentials() { @Override @@ -262,67 +223,43 @@ public void setupTestQueuesAndTopics() { } private void setUpQueueAndTopics() { - amazonSQSAsync.createQueue(repoIncomingQueueName); - - var fragmentQueue = amazonSQSAsync.createQueue(largeMessageFragmentsQueueName); - var fragmentsTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_large_message_fragments_topic").build()); - createSnsTestReceiverSubscription(fragmentsTopic, getQueueArn(fragmentQueue.getQueueUrl())); - - var transferCompleteQueue = amazonSQSAsync.createQueue(transferCompleteQueueName); - var transferCompleteTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_transfer_complete_topic").build()); - createSnsTestReceiverSubscription(transferCompleteTopic, getQueueArn(transferCompleteQueue.getQueueUrl())); - - var fragmentObservabilityQueue = amazonSQSAsync.createQueue(largeMessageFragmentsObservabilityQueueName); - createSnsTestReceiverSubscription(fragmentsTopic, getQueueArn(fragmentObservabilityQueue.getQueueUrl())); - - var smallEhrQueue = amazonSQSAsync.createQueue(smallEhrQueueName); - var smallEhrTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_small_ehr_topic").build()); - createSnsTestReceiverSubscription(smallEhrTopic, getQueueArn(smallEhrQueue.getQueueUrl())); - - var smallEhrObservabilityQueue = amazonSQSAsync.createQueue(smallEhrObservabilityQueueName); - createSnsTestReceiverSubscription(smallEhrTopic, getQueueArn(smallEhrObservabilityQueue.getQueueUrl())); - - var largeEhrQueue = amazonSQSAsync.createQueue(largeEhrQueueName); - var largeEhrTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_large_ehr_topic").build()); - createSnsTestReceiverSubscription(largeEhrTopic, getQueueArn(largeEhrQueue.getQueueUrl())); - - var positiveAcksTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_positive_acks_topic").build()); - var positiveAcksQueue = amazonSQSAsync.createQueue(positiveAcksQueueName); - createSnsTestReceiverSubscription(positiveAcksTopic, getQueueArn(positiveAcksQueue.getQueueUrl())); - - var parsingDlqTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_dlq_topic").build()); - var parsingDlqQueue = amazonSQSAsync.createQueue(parsingDlqQueueName); - createSnsTestReceiverSubscription(parsingDlqTopic, getQueueArn(parsingDlqQueue.getQueueUrl())); - - var ehrCompleteQueue = amazonSQSAsync.createQueue(ehrCompleteQueueName); - var ehrCompleteTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_ehr_complete_topic").build()); - createSnsTestReceiverSubscription(ehrCompleteTopic, getQueueArn(ehrCompleteQueue.getQueueUrl())); - - var nackTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_negative_acks_topic").build()); - var nackQueue = amazonSQSAsync.createQueue(nackInternalQueueName); - createSnsTestReceiverSubscription(nackTopic, getQueueArn(nackQueue.getQueueUrl())); - - var ehrInUnhandledTopic = snsClient.createTopic(CreateTopicRequest.builder().name("test_ehr_in_unhandled_topic").build()); - var ehrInUnhandledObservabilityQueue = amazonSQSAsync.createQueue("ehr_in_unhandled_queue"); - createSnsTestReceiverSubscription(ehrInUnhandledTopic, getQueueArn(ehrInUnhandledObservabilityQueue.getQueueUrl())); - - snsClient.createTopic(CreateTopicRequest.builder().name("test_splunk_uploader_topic").build()); + createQueue(repoIncomingQueueName); + + createSnsTopic("test_splunk_uploader_topic"); + + createQueueAndSnsReceiverSubscription(transferCompleteQueueName, "test_transfer_complete_topic"); + createQueueAndSnsReceiverSubscription(largeEhrQueueName, "test_large_ehr_topic"); + createQueueAndSnsReceiverSubscription(positiveAcksQueueName, "test_positive_acks_topic"); + createQueueAndSnsReceiverSubscription(parsingDlqQueueName, "test_dlq_topic"); + createQueueAndSnsReceiverSubscription(ehrCompleteQueueName, "test_ehr_complete_topic"); + createQueueAndSnsReceiverSubscription(nackInternalQueueName, "test_negative_acks_topic"); + createQueueAndSnsReceiverSubscription("ehr_in_unhandled_queue", "test_ehr_in_unhandled_topic"); + + createQueueAndObservabilityQueueAndSnsReceiverSubscriptions( + largeMessageFragmentsQueueName, + largeMessageFragmentsObservabilityQueueName, + "test_large_message_fragments_topic"); + + createQueueAndObservabilityQueueAndSnsReceiverSubscriptions( + smallEhrQueueName, + smallEhrObservabilityQueueName, + "test_small_ehr_topic" + ); } private void setupS3Bucket() { - var waiter = s3Client.waiter(); var createBucketRequest = CreateBucketRequest.builder() .bucket(sqsLargeMessageBucketName) .build(); - for (var bucket: s3Client.listBuckets().buckets()) { + for (var bucket : s3Client.listBuckets().buckets()) { if (Objects.equals(bucket.name(), sqsLargeMessageBucketName)) { return; } } s3Client.createBucket(createBucketRequest); - waiter.waitUntilBucketExists(HeadBucketRequest.builder().bucket(sqsLargeMessageBucketName).build()); + s3Client.waiter().waitUntilBucketExists(HeadBucketRequest.builder().bucket(sqsLargeMessageBucketName).build()); } private void createDynamoTable() { @@ -345,9 +282,9 @@ private void createDynamoTable() { // Sort Key keySchema.add(KeySchemaElement.builder() - .keyType(KeyType.RANGE) - .attributeName(LAYER.name) - .build()); + .keyType(KeyType.RANGE) + .attributeName(LAYER.name) + .build()); final List attributeDefinitions = new ArrayList<>(); @@ -362,54 +299,54 @@ private void createDynamoTable() { .build()); attributeDefinitions.add(AttributeDefinition.builder() - .attributeType(ScalarAttributeType.S) - .attributeName(OUTBOUND_CONVERSATION_ID.name) - .build()); + .attributeType(ScalarAttributeType.S) + .attributeName(OUTBOUND_CONVERSATION_ID.name) + .build()); attributeDefinitions.add(AttributeDefinition.builder() .attributeType(ScalarAttributeType.S) .attributeName(NHS_NUMBER.name) - .build()); + .build()); // NHS Number GSI final List globalSecondaryIndexes = List.of( - GlobalSecondaryIndex.builder() - .indexName("NhsNumberSecondaryIndex") - .keySchema(KeySchemaElement.builder() - .keyType(KeyType.HASH) - .attributeName(NHS_NUMBER.name) - .build()) - .projection(Projection.builder().projectionType(ProjectionType.ALL).build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(DYNAMO_READ_CAPACITY_UNITS) - .writeCapacityUnits(DYNAMO_WRITE_CAPACITY_UNITS) - .build()) - .build(), - - GlobalSecondaryIndex.builder() - .indexName("OutboundConversationIdSecondaryIndex") - .keySchema(KeySchemaElement.builder() - .keyType(KeyType.HASH) - .attributeName(OUTBOUND_CONVERSATION_ID.name) - .build()) - .projection(Projection.builder().projectionType(ProjectionType.ALL).build()) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(DYNAMO_READ_CAPACITY_UNITS) - .writeCapacityUnits(DYNAMO_WRITE_CAPACITY_UNITS) - .build()) - .build() + GlobalSecondaryIndex.builder() + .indexName("NhsNumberSecondaryIndex") + .keySchema(KeySchemaElement.builder() + .keyType(KeyType.HASH) + .attributeName(NHS_NUMBER.name) + .build()) + .projection(Projection.builder().projectionType(ProjectionType.ALL).build()) + .provisionedThroughput(ProvisionedThroughput.builder() + .readCapacityUnits(DYNAMO_READ_CAPACITY_UNITS) + .writeCapacityUnits(DYNAMO_WRITE_CAPACITY_UNITS) + .build()) + .build(), + + GlobalSecondaryIndex.builder() + .indexName("OutboundConversationIdSecondaryIndex") + .keySchema(KeySchemaElement.builder() + .keyType(KeyType.HASH) + .attributeName(OUTBOUND_CONVERSATION_ID.name) + .build()) + .projection(Projection.builder().projectionType(ProjectionType.ALL).build()) + .provisionedThroughput(ProvisionedThroughput.builder() + .readCapacityUnits(DYNAMO_READ_CAPACITY_UNITS) + .writeCapacityUnits(DYNAMO_WRITE_CAPACITY_UNITS) + .build()) + .build() ); final CreateTableRequest createTableRequest = CreateTableRequest.builder() - .tableName(transferTrackerDbTableName) - .keySchema(keySchema) - .globalSecondaryIndexes(globalSecondaryIndexes) - .attributeDefinitions(attributeDefinitions) - .provisionedThroughput(ProvisionedThroughput.builder() - .readCapacityUnits(DYNAMO_READ_CAPACITY_UNITS) - .writeCapacityUnits(DYNAMO_WRITE_CAPACITY_UNITS) - .build() - ).build(); + .tableName(transferTrackerDbTableName) + .keySchema(keySchema) + .globalSecondaryIndexes(globalSecondaryIndexes) + .attributeDefinitions(attributeDefinitions) + .provisionedThroughput(ProvisionedThroughput.builder() + .readCapacityUnits(DYNAMO_READ_CAPACITY_UNITS) + .writeCapacityUnits(DYNAMO_WRITE_CAPACITY_UNITS) + .build() + ).build(); dynamoDbClient.createTable(createTableRequest); waiter.waitUntilTableExists(tableRequest); @@ -417,13 +354,49 @@ private void createDynamoTable() { private void deleteDynamoTable(DynamoDbWaiter waiter, DescribeTableRequest tableRequest) { final DeleteTableRequest deleteRequest = DeleteTableRequest.builder() - .tableName(transferTrackerDbTableName) - .build(); + .tableName(transferTrackerDbTableName) + .build(); dynamoDbClient.deleteTable(deleteRequest); waiter.waitUntilTableNotExists(tableRequest); } + private void createQueueAndSnsReceiverSubscription(String queueName, String snsName) { + String queueUrl = createQueue(queueName); + CreateTopicResponse topic = createSnsTopic(snsName); + createSnsTestReceiverSubscription(topic, getQueueArn(queueUrl)); + } + + private void createQueueAndObservabilityQueueAndSnsReceiverSubscriptions( + String queueName, + String observabilityQueueName, + String snsName + ) { + String queueUrl = createQueue(queueName); + String observabilityQueueUrl = createQueue(observabilityQueueName); + + CreateTopicResponse topic = createSnsTopic(snsName); + + createSnsTestReceiverSubscription(topic, getQueueArn(queueUrl)); + createSnsTestReceiverSubscription(topic, getQueueArn(observabilityQueueUrl)); + } + + private String createQueue(String queueName) { + CreateQueueRequest createQueueRequest = CreateQueueRequest.builder() + .queueName(queueName) + .build(); + + return sqsClient.createQueue(createQueueRequest).queueUrl(); + } + + private CreateTopicResponse createSnsTopic(String snsName) { + CreateTopicRequest createTopicRequest = CreateTopicRequest.builder() + .name(snsName) + .build(); + + return snsClient.createTopic(createTopicRequest); + } + private void createSnsTestReceiverSubscription(CreateTopicResponse topic, String queueArn) { final Map attributes = new HashMap<>(); attributes.put("RawMessageDelivery", "True"); @@ -436,9 +409,11 @@ private void createSnsTestReceiverSubscription(CreateTopicResponse topic, String snsClient.subscribe(subscribeRequest); } - private String getQueueArn(String queueUrl) { - var queueAttributes = amazonSQSAsync.getQueueAttributes(queueUrl, List.of("QueueArn")); - return queueAttributes.getAttributes().get("QueueArn"); + GetQueueAttributesRequest request = GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames(QueueAttributeName.QUEUE_ARN) + .build(); + return sqsClient.getQueueAttributes(request).attributes().get(QueueAttributeName.QUEUE_ARN); } -} \ No newline at end of file +} diff --git a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/handler/NegativeAcknowledgmentHandlingIntegrationTest.java b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/handler/NegativeAcknowledgmentHandlingIntegrationTest.java index 46963f3e..26a4143d 100644 --- a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/handler/NegativeAcknowledgmentHandlingIntegrationTest.java +++ b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/handler/NegativeAcknowledgmentHandlingIntegrationTest.java @@ -1,7 +1,7 @@ package uk.nhs.prm.repo.ehrtransferservice.handler; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -38,7 +38,7 @@ public class NegativeAcknowledgmentHandlingIntegrationTest { TransferService transferService; @Autowired - private AmazonSQSAsync sqs; + private SqsClient sqs; @Value("${activemq.inboundQueue}") private String inboundQueue; @@ -91,9 +91,7 @@ private UUID createConversationRecord() { } private void purgeQueue(String queueName) { - final String queueUrl = sqs.getQueueUrl(queueName).getQueueUrl(); - final PurgeQueueRequest purgeQueueRequest = new PurgeQueueRequest(queueUrl); - - sqs.purgeQueue(purgeQueueRequest); + final String queueUrl = sqs.getQueueUrl(builder -> builder.queueName(queueName)).queueUrl(); + sqs.purgeQueue(PurgeQueueRequest.builder().queueUrl(queueUrl).build()); } } \ No newline at end of file diff --git a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/parsers/ParserBrokerIntegrationTest.java b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/parsers/ParserBrokerIntegrationTest.java index 0f6f672c..0ae30258 100644 --- a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/parsers/ParserBrokerIntegrationTest.java +++ b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/parsers/ParserBrokerIntegrationTest.java @@ -1,9 +1,12 @@ package uk.nhs.prm.repo.ehrtransferservice.parsers; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.Message; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; -import com.amazonaws.services.sqs.model.ReceiveMessageRequest; +import org.junit.jupiter.api.BeforeEach; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.Message; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -20,9 +23,13 @@ import uk.nhs.prm.repo.ehrtransferservice.database.TransferService; import uk.nhs.prm.repo.ehrtransferservice.exceptions.ConversationIneligibleForRetryException; import uk.nhs.prm.repo.ehrtransferservice.repo_incoming.RepoIncomingEvent; +import uk.nhs.prm.repo.ehrtransferservice.services.PresignedUrl; +import uk.nhs.prm.repo.ehrtransferservice.services.ehr_repo.EhrRepoClient; +import uk.nhs.prm.repo.ehrtransferservice.services.ehr_repo.EhrRepoService; import uk.nhs.prm.repo.ehrtransferservice.utils.TransferTrackerDbUtility; import java.io.IOException; +import java.net.URL; import java.util.List; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -30,6 +37,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.junit.jupiter.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; import static uk.nhs.prm.repo.ehrtransferservice.database.enumeration.ConversationTransferStatus.INBOUND_REQUEST_SENT; import static uk.nhs.prm.repo.ehrtransferservice.database.enumeration.Layer.CONVERSATION; import static uk.nhs.prm.repo.ehrtransferservice.utils.TestDataLoaderUtility.getTestDataAsString; @@ -41,7 +50,7 @@ @ContextConfiguration(classes = LocalStackAwsConfig.class) public class ParserBrokerIntegrationTest { @Autowired - private AmazonSQSAsync sqs; + private SqsClient sqs; @Autowired private TransferService transferService; @@ -49,6 +58,9 @@ public class ParserBrokerIntegrationTest { @Autowired TransferTrackerDbUtility transferTrackerDbUtility; + @MockitoBean + private EhrRepoService ehrRepoService; + @Value("${activemq.inboundQueue}") private String inboundQueue; @@ -76,6 +88,12 @@ public class ParserBrokerIntegrationTest { private static final UUID NEMS_MESSAGE_ID = UUID.fromString("ad9246ce-b337-4ba9-973f-e1284e1f79c7"); private static final String NHS_NUMBER = "9896589658"; + @BeforeEach + void configureMocks() throws Exception { + purgeQueue(largeMessageFragmentsObservabilityQueueName); + purgeQueue(smallEhrObservabilityQueueName); + } + @AfterEach public void tearDown() { purgeQueue(largeMessageFragmentsObservabilityQueueName); @@ -100,7 +118,7 @@ void shouldPublishCopcMessageToLargeMessageFragmentTopic() throws IOException { final RepoIncomingEvent repoIncomingEvent = createDefaultRepoIncomingEvent(COPC_INBOUND_CONVERSATION_ID); final String fragmentMessageBody = getTestDataAsString("large-ehr-fragment-with-ref"); final SimpleAmqpQueue inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); - final String fragmentsQueueUrl = sqs.getQueueUrl(largeMessageFragmentsObservabilityQueueName).getQueueUrl(); + final String fragmentsQueueUrl = getQueueUrl(largeMessageFragmentsObservabilityQueueName); // when try { @@ -113,10 +131,10 @@ void shouldPublishCopcMessageToLargeMessageFragmentTopic() throws IOException { // then await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var receivedMessageHolder = checkMessageInRelatedQueue(fragmentsQueueUrl); - Assertions.assertTrue(receivedMessageHolder.get(0).getBody().contains(fragmentMessageBody)); - Assertions.assertTrue(receivedMessageHolder.get(0).getMessageAttributes().containsKey("traceId")); - Assertions.assertTrue(receivedMessageHolder.get(0).getMessageAttributes().containsKey("conversationId")); + List receivedMessageHolder = checkMessageInRelatedQueue(fragmentsQueueUrl); + Assertions.assertTrue(receivedMessageHolder.getFirst().body().contains(fragmentMessageBody)); + Assertions.assertTrue(receivedMessageHolder.getFirst().messageAttributes().containsKey("traceId")); + Assertions.assertTrue(receivedMessageHolder.getFirst().messageAttributes().containsKey("conversationId")); }); } @@ -126,7 +144,7 @@ void shouldPublishEhrCoreToSmallEhrObservabilityQueue() throws IOException { final RepoIncomingEvent repoIncomingEvent = createDefaultRepoIncomingEvent(EHR_CORE_INBOUND_CONVERSATION_ID); final String ehrCoreMessageBody = getTestDataAsString("small-ehr"); final SimpleAmqpQueue inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); - final String smallEhrObservabilityQueueUrl = sqs.getQueueUrl(smallEhrObservabilityQueueName).getQueueUrl(); + final String smallEhrObservabilityQueueUrl = getQueueUrl(smallEhrObservabilityQueueName); // when try { @@ -143,10 +161,10 @@ void shouldPublishEhrCoreToSmallEhrObservabilityQueue() throws IOException { inboundQueueFromMhs.sendMessage(ehrCoreMessageBody); await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var receivedMessageHolder = checkMessageInRelatedQueue(smallEhrObservabilityQueueUrl); - Assertions.assertTrue(receivedMessageHolder.get(0).getBody().contains(ehrCoreMessageBody)); - Assertions.assertTrue(receivedMessageHolder.get(0).getMessageAttributes().containsKey("traceId")); - Assertions.assertTrue(receivedMessageHolder.get(0).getMessageAttributes().containsKey("conversationId")); + List receivedMessageHolder = checkMessageInRelatedQueue(smallEhrObservabilityQueueUrl); + Assertions.assertTrue(receivedMessageHolder.getFirst().body().contains(ehrCoreMessageBody)); + Assertions.assertTrue(receivedMessageHolder.getFirst().messageAttributes().containsKey("traceId")); + Assertions.assertTrue(receivedMessageHolder.getFirst().messageAttributes().containsKey("conversationId")); }); } @@ -156,7 +174,7 @@ void shouldPassCorrelationIdToBeSetAsTraceId() throws IOException { final RepoIncomingEvent repoIncomingEvent = createDefaultRepoIncomingEvent(EHR_CORE_INBOUND_CONVERSATION_ID); final String ehrCoreMessageBody = getTestDataAsString("small-ehr"); final SimpleAmqpQueue inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); - final String smallEhrObservabilityQueueUrl = sqs.getQueueUrl(smallEhrObservabilityQueueName).getQueueUrl(); + final String smallEhrObservabilityQueueUrl = getQueueUrl(smallEhrObservabilityQueueName); final String correlationId = UUID.randomUUID().toString(); // when @@ -174,58 +192,66 @@ void shouldPassCorrelationIdToBeSetAsTraceId() throws IOException { inboundQueueFromMhs.sendMessage(ehrCoreMessageBody, correlationId); await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var receivedMessageHolder = checkMessageInRelatedQueue(smallEhrObservabilityQueueUrl); - var message = receivedMessageHolder.get(0); - Assertions.assertTrue(message.getBody().contains(ehrCoreMessageBody)); - Assertions.assertTrue(message.getMessageAttributes().containsKey("traceId")); - Assertions.assertEquals(message.getMessageAttributes().get("traceId").getStringValue(), correlationId); + List receivedMessageHolder = checkMessageInRelatedQueue(smallEhrObservabilityQueueUrl); + Message message = receivedMessageHolder.getFirst(); + Assertions.assertTrue(message.body().contains(ehrCoreMessageBody)); + Assertions.assertTrue(message.messageAttributes().containsKey("traceId")); + Assertions.assertEquals(message.messageAttributes().get("traceId").stringValue(), correlationId); }); } @Test void shouldPublishInvalidMessageToDlq() { - var wrongMessage = "something wrong"; + String wrongMessage = "something wrong"; - var inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); + SimpleAmqpQueue inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); inboundQueueFromMhs.sendMessage(wrongMessage); - var parsingDqlQueueUrl = sqs.getQueueUrl(parsingDlqQueueName).getQueueUrl(); + String parsingDqlQueueUrl = getQueueUrl(parsingDlqQueueName); await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var receivedMessageHolder = checkMessageInRelatedQueue(parsingDqlQueueUrl); - Assertions.assertTrue(receivedMessageHolder.get(0).getBody().contains(wrongMessage)); + List receivedMessageHolder = checkMessageInRelatedQueue(parsingDqlQueueUrl); + Assertions.assertTrue(receivedMessageHolder.getFirst().body().contains(wrongMessage)); }); } @Test void shouldPublishUnprocessableMessageToDlq() { - var unprocessableMessage = "NO_ACTION:UNPROCESSABLE_MESSAGE_BODY"; - var inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); + String unprocessableMessage = "NO_ACTION:UNPROCESSABLE_MESSAGE_BODY"; + SimpleAmqpQueue inboundQueueFromMhs = new SimpleAmqpQueue(inboundQueue); inboundQueueFromMhs.sendUnprocessableAmqpMessage(); - var parsingDqlQueueUrl = sqs.getQueueUrl(parsingDlqQueueName).getQueueUrl(); + String parsingDqlQueueUrl = getQueueUrl(parsingDlqQueueName); await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var receivedMessageHolder = checkMessageInRelatedQueue(parsingDqlQueueUrl); - Assertions.assertTrue(receivedMessageHolder.get(0).getBody().contains(unprocessableMessage)); + List receivedMessageHolder = checkMessageInRelatedQueue(parsingDqlQueueUrl); + Assertions.assertTrue(receivedMessageHolder.getFirst().body().contains(unprocessableMessage)); }); } private List checkMessageInRelatedQueue(String queueUrl) { System.out.println("checking sqs queue: " + queueUrl); - var requestForMessagesWithAttributes - = new ReceiveMessageRequest().withQueueUrl(queueUrl) - .withMessageAttributeNames("All"); - var messages = sqs.receiveMessage(requestForMessagesWithAttributes).getMessages(); + ReceiveMessageRequest requestForMessagesWithAttributes = ReceiveMessageRequest.builder() + .queueUrl(queueUrl) + .messageAttributeNames("All") + .build(); + + List messages = sqs.receiveMessage(requestForMessagesWithAttributes).messages(); + System.out.println("messages in checkMessageInRelatedQueue: " + messages); assertThat(messages).hasSize(1); return messages; } private void purgeQueue(String queueName) { - var queueUrl = sqs.getQueueUrl(queueName).getQueueUrl(); - sqs.purgeQueue(new PurgeQueueRequest(queueUrl)); + String queueUrl = getQueueUrl(queueName); + sqs.purgeQueue(PurgeQueueRequest.builder().queueUrl(queueUrl).build()); + + } + + private String getQueueUrl(String queueName) { + return sqs.getQueueUrl(builder -> builder.queueName(queueName)).queueUrl(); } private RepoIncomingEvent createDefaultRepoIncomingEvent(UUID inboundConversationId) { diff --git a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/utils/SqsQueueUtility.java b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/utils/SqsQueueUtility.java index ca5b841c..0f43088d 100644 --- a/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/utils/SqsQueueUtility.java +++ b/services/ehr-transfer-service/src/integration/java/uk/nhs/prm/repo/ehrtransferservice/utils/SqsQueueUtility.java @@ -1,7 +1,9 @@ package uk.nhs.prm.repo.ehrtransferservice.utils; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; +import software.amazon.awssdk.services.sqs.model.SendMessageRequest; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; @@ -9,30 +11,37 @@ @Component public final class SqsQueueUtility { - private final AmazonSQSAsync amazonSQSAsync; + private final SqsClient sqsClient; private static final Logger LOGGER = LogManager.getLogger(SqsQueueUtility.class); @Autowired - public SqsQueueUtility(AmazonSQSAsync amazonSQSAsync) { - this.amazonSQSAsync = amazonSQSAsync; + public SqsQueueUtility(SqsClient sqsClient) { + this.sqsClient = sqsClient; } public void purgeQueue(String queueName) { final String queueUrl = getQueueUrl(queueName); - final PurgeQueueRequest request = new PurgeQueueRequest(queueUrl); - - amazonSQSAsync.purgeQueue(request); + final PurgeQueueRequest request = PurgeQueueRequest.builder() + .queueUrl(queueUrl) + .build(); + sqsClient.purgeQueue(request); LOGGER.info("Successfully purged queue - {}", queueUrl); } public void sendSqsMessage(String message, String queueName) { final String queueUrl = getQueueUrl(queueName); - amazonSQSAsync.sendMessage(queueUrl, message); - + SendMessageRequest request = SendMessageRequest.builder() + .queueUrl(queueUrl) + .messageBody(message) + .build(); + sqsClient.sendMessage(request); LOGGER.info("Message sent successfully to {} SQS queue", queueName); } private String getQueueUrl(String queueName) { - return amazonSQSAsync.getQueueUrl(queueName).getQueueUrl(); + GetQueueUrlRequest queueUrlRequest = GetQueueUrlRequest.builder() + .queueName(queueName) + .build(); + return sqsClient.getQueueUrl(queueUrlRequest).queueUrl(); } } \ No newline at end of file diff --git a/services/ehr-transfer-service/src/integration/resources/logback-test.xml b/services/ehr-transfer-service/src/integration/resources/logback-test.xml new file mode 100644 index 00000000..0e5b2f6f --- /dev/null +++ b/services/ehr-transfer-service/src/integration/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/ActiveMQConfig.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/ActiveMQConfig.java index 38d933e3..b7ca92be 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/ActiveMQConfig.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/ActiveMQConfig.java @@ -4,7 +4,6 @@ import org.apache.activemq.ActiveMQConnectionFactory; import org.apache.activemq.ActiveMQSession; import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jms.config.DefaultJmsListenerContainerFactory; @@ -30,13 +29,10 @@ public class ActiveMQConfig { private String randomOption; @Bean - public DefaultJmsListenerContainerFactory myFactory(ConnectionFactory connectionFactory, - DefaultJmsListenerContainerFactoryConfigurer configurer) { + public DefaultJmsListenerContainerFactory myFactory(ConnectionFactory connectionFactory) { DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory(); + factory.setConnectionFactory(connectionFactory); factory.setSessionAcknowledgeMode(ActiveMQSession.INDIVIDUAL_ACKNOWLEDGE); - // This provides all boot's default to this factory, including the message converter - configurer.configure(factory, connectionFactory); - // You could still override some of Boot's default if necessary. return factory; } diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/S3ClientSpringConfiguration.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/S3ClientSpringConfiguration.java index 5b0a3b5d..ac15734b 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/S3ClientSpringConfiguration.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/S3ClientSpringConfiguration.java @@ -1,7 +1,5 @@ package uk.nhs.prm.repo.ehrtransferservice.config; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3ClientBuilder; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -26,9 +24,4 @@ public S3Client amazonS3Client() { .credentialsProvider(awsCredentialsProvider) .build(); } - - @Bean - public AmazonS3 amazonS3ClientForSnsExtended() { - return AmazonS3ClientBuilder.standard().withRegion(awsRegion).build(); - } } \ No newline at end of file diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsClientSpringConfiguration.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsClientSpringConfiguration.java index 3045a0b6..edc4c922 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsClientSpringConfiguration.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsClientSpringConfiguration.java @@ -1,7 +1,5 @@ package uk.nhs.prm.repo.ehrtransferservice.config; -import com.amazonaws.services.sns.AmazonSNS; -import com.amazonaws.services.sns.AmazonSNSClientBuilder; import lombok.RequiredArgsConstructor; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -19,14 +17,7 @@ public class SnsClientSpringConfiguration { private AwsCredentialsProvider awsCredentialsProvider; @Bean - public AmazonSNS snsClient() { - return AmazonSNSClientBuilder.standard() - .withRegion(awsRegion) - .build(); - } - - @Bean - public SnsClient snsClientV2() { + public SnsClient snsClient() { return SnsClient.builder() .region(Region.of(awsRegion)) .credentialsProvider(awsCredentialsProvider) diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsExtendedClient.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsExtendedClient.java deleted file mode 100644 index f627043f..00000000 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SnsExtendedClient.java +++ /dev/null @@ -1,22 +0,0 @@ -package uk.nhs.prm.repo.ehrtransferservice.config; - -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.sns.AmazonSNS; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import software.amazon.sns.AmazonSNSExtendedClient; -import software.amazon.sns.SNSExtendedClientConfiguration; - -@Configuration -public class SnsExtendedClient { - @Value("${aws.sqsLargeMessageBucketName}") - private String bucketName; - - @Bean - public AmazonSNSExtendedClient s3SupportedSnsClient(AmazonSNS snsClient, AmazonS3 s3) { - var snsExtendedClientConfiguration = new SNSExtendedClientConfiguration() - .withPayloadSupportEnabled(s3, bucketName); - return new AmazonSNSExtendedClient(snsClient, snsExtendedClientConfiguration); - } -} diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsClientSpringConfiguration.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsClientSpringConfiguration.java index 200b26a3..312dbb45 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsClientSpringConfiguration.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsClientSpringConfiguration.java @@ -1,7 +1,5 @@ package uk.nhs.prm.repo.ehrtransferservice.config; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import software.amazon.awssdk.services.sqs.SqsClient; @@ -9,11 +7,8 @@ @Configuration public class SqsClientSpringConfiguration { - @Bean - public AmazonSQSAsync amazonSQSAsync() { - return AmazonSQSAsyncClientBuilder.defaultClient(); - } + @Bean public SqsClient sqsClient() { return SqsClient.create(); diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsExtendedClient.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsExtendedClient.java deleted file mode 100644 index 30c8b25b..00000000 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsExtendedClient.java +++ /dev/null @@ -1,24 +0,0 @@ -package uk.nhs.prm.repo.ehrtransferservice.config; - -import com.amazon.sqs.javamessaging.AmazonSQSExtendedClient; -import com.amazon.sqs.javamessaging.ExtendedClientConfiguration; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import lombok.RequiredArgsConstructor; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -@RequiredArgsConstructor -public class SqsExtendedClient { - - @Value("${aws.sqsLargeMessageBucketName}") - private String bucketName; - - @Bean - public AmazonSQSExtendedClient s3SupportedSqsClient(AmazonSQSAsync sqsClient, AmazonS3 s3) { - var extendedClientConfiguration = new ExtendedClientConfiguration().withPayloadSupportEnabled(s3, bucketName, true); - return new AmazonSQSExtendedClient(sqsClient, extendedClientConfiguration); - } -} diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsListenerSpringConfiguration.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsListenerSpringConfiguration.java index d2fc50bb..b79e2d5b 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsListenerSpringConfiguration.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/config/SqsListenerSpringConfiguration.java @@ -4,8 +4,7 @@ import com.amazon.sqs.javamessaging.SQSConnection; import com.amazon.sqs.javamessaging.SQSConnectionFactory; import com.amazon.sqs.javamessaging.SQSSession; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; +import software.amazon.awssdk.services.sqs.SqsClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -26,8 +25,8 @@ import uk.nhs.prm.repo.ehrtransferservice.repo_incoming.RepoIncomingEventParser; import uk.nhs.prm.repo.ehrtransferservice.repo_incoming.RepoIncomingService; -import javax.jms.JMSException; -import javax.jms.Session; +import jakarta.jms.JMSException; +import jakarta.jms.Session; @Configuration @RequiredArgsConstructor @@ -59,13 +58,13 @@ public class SqsListenerSpringConfiguration { private String negativeAckQueueName; @Bean - public AmazonSQSAsync amazonSQSAsync() { - return AmazonSQSAsyncClientBuilder.defaultClient(); + public SqsClient sqsClient() { + return SqsClient.create(); } @Bean - public SQSConnection createConnection(AmazonSQSAsync amazonSQSAsync) throws JMSException { - var connectionFactory = new SQSConnectionFactory(new ProviderConfiguration(), amazonSQSAsync); + public SQSConnection createConnection(SqsClient sqsClient) throws JMSException { + SQSConnectionFactory connectionFactory = new SQSConnectionFactory(new ProviderConfiguration(), sqsClient); return connectionFactory.createConnection(); } diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/Gp2gpMessengerPositiveAcknowledgementRequestBody.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/Gp2gpMessengerPositiveAcknowledgementRequestBody.java index 2944e4e2..0d1a6b11 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/Gp2gpMessengerPositiveAcknowledgementRequestBody.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/Gp2gpMessengerPositiveAcknowledgementRequestBody.java @@ -1,9 +1,10 @@ package uk.nhs.prm.repo.ehrtransferservice.gp2gp_message_models; -import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; +import lombok.EqualsAndHashCode; @Data +@EqualsAndHashCode(callSuper=true) public class Gp2gpMessengerPositiveAcknowledgementRequestBody extends Gp2gpMessengerAcknowledgementRequestBody{ public Gp2gpMessengerPositiveAcknowledgementRequestBody( String repositoryAsid, diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/ParsedMessage.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/ParsedMessage.java index 9f092734..dd91eaa8 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/ParsedMessage.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/gp2gp_message_models/ParsedMessage.java @@ -1,10 +1,5 @@ package uk.nhs.prm.repo.ehrtransferservice.gp2gp_message_models; -import org.apache.activemq.command.ActiveMQBytesMessage; - -import javax.jms.BytesMessage; -import javax.jms.JMSException; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.UUID; diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/NegativeAcknowledgementListener.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/NegativeAcknowledgementListener.java index 91b52def..55ed23d2 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/NegativeAcknowledgementListener.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/NegativeAcknowledgementListener.java @@ -1,5 +1,8 @@ package uk.nhs.prm.repo.ehrtransferservice.listeners; +import jakarta.jms.Message; +import jakarta.jms.MessageListener; +import jakarta.jms.TextMessage; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; @@ -7,10 +10,6 @@ import uk.nhs.prm.repo.ehrtransferservice.models.ack.Acknowledgement; import uk.nhs.prm.repo.ehrtransferservice.parsers.Parser; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.TextMessage; - @Slf4j @RequiredArgsConstructor public class NegativeAcknowledgementListener implements MessageListener { diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/S3ExtendedMessageListener.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/S3ExtendedMessageListener.java index 832f63b1..36a02af0 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/S3ExtendedMessageListener.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/listeners/S3ExtendedMessageListener.java @@ -1,5 +1,6 @@ package uk.nhs.prm.repo.ehrtransferservice.listeners; +import jakarta.jms.JMSException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; @@ -8,9 +9,8 @@ import uk.nhs.prm.repo.ehrtransferservice.handlers.MessageHandler; import uk.nhs.prm.repo.ehrtransferservice.parsers.S3ExtendedMessageFetcher; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; +import jakarta.jms.Message; +import jakarta.jms.MessageListener; @Slf4j @RequiredArgsConstructor diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/logging/Tracer.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/logging/Tracer.java index bc51f05c..2c684fb0 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/logging/Tracer.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/logging/Tracer.java @@ -1,12 +1,12 @@ package uk.nhs.prm.repo.ehrtransferservice.logging; +import jakarta.jms.JMSException; +import jakarta.jms.Message; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.slf4j.MDC; import org.springframework.context.annotation.Configuration; -import javax.jms.JMSException; -import javax.jms.Message; import static uk.nhs.prm.repo.ehrtransferservice.logging.TraceKey.CONVERSATION_ID; import static uk.nhs.prm.repo.ehrtransferservice.logging.TraceKey.TRACE_ID; diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/message_publishers/MessagePublisher.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/message_publishers/MessagePublisher.java index 2f7cfff0..a8afaefd 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/message_publishers/MessagePublisher.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/message_publishers/MessagePublisher.java @@ -1,12 +1,13 @@ package uk.nhs.prm.repo.ehrtransferservice.message_publishers; -import com.amazonaws.services.sns.model.MessageAttributeValue; -import com.amazonaws.services.sns.model.PublishRequest; import com.google.gson.Gson; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; -import software.amazon.sns.AmazonSNSExtendedClient; +import software.amazon.awssdk.services.sns.SnsClient; +import software.amazon.awssdk.services.sns.model.MessageAttributeValue; +import software.amazon.awssdk.services.sns.model.PublishRequest; +import software.amazon.awssdk.services.sns.model.PublishResponse; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; import java.util.HashMap; @@ -16,7 +17,7 @@ @Slf4j @RequiredArgsConstructor public class MessagePublisher { - private final AmazonSNSExtendedClient snsClient; + private final SnsClient snsClient; private final Tracer tracer; public void sendMessage(String topicArn, String message) { @@ -30,14 +31,15 @@ public void sendMessage(String topicArn, String message, Map att attributes.forEach((key, value) -> messageAttributes.put(key, getMessageAttributeValue(value))); } - PublishRequest request = new PublishRequest() - .withMessage(message) - .withMessageAttributes(messageAttributes) - .withTopicArn(topicArn); + PublishRequest request = PublishRequest.builder() + .message(message) + .messageAttributes(messageAttributes) + .topicArn(topicArn) + .build(); - var result = snsClient.publish(request); - var topicAttributes = topicArn.split(":"); - log.info("PUBLISHED: message to {} topic. Published SNS message id: {}", topicAttributes[topicAttributes.length - 1], result.getMessageId()); + PublishResponse response = snsClient.publish(request); + String[] topicAttributes = topicArn.split(":"); + log.info("PUBLISHED: message to {} topic. Published SNS message id: {}", topicAttributes[topicAttributes.length - 1], response.messageId()); } public void sendJsonMessage(String topicArn, Object message, Map attributes) { @@ -46,6 +48,9 @@ public void sendJsonMessage(String topicArn, Object message, Map } private MessageAttributeValue getMessageAttributeValue(String attributeValue) { - return new MessageAttributeValue().withDataType("String").withStringValue(attributeValue); + return MessageAttributeValue.builder() + .dataType("String") + .stringValue(attributeValue) + .build(); } } diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcher.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcher.java index 56633061..a700a60a 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcher.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcher.java @@ -10,8 +10,8 @@ import uk.nhs.prm.repo.ehrtransferservice.models.S3PointerMessage; import uk.nhs.prm.repo.ehrtransferservice.models.enums.Status; -import javax.jms.Message; -import javax.jms.TextMessage; +import jakarta.jms.Message; +import jakarta.jms.TextMessage; import java.io.BufferedReader; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; diff --git a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListener.java b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListener.java index 0d3d59a5..541f3423 100644 --- a/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListener.java +++ b/services/ehr-transfer-service/src/main/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListener.java @@ -7,10 +7,10 @@ import uk.nhs.prm.repo.ehrtransferservice.exceptions.acknowledgement.EhrCompleteAcknowledgementFailedException; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; -import javax.jms.JMSException; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.TextMessage; +import jakarta.jms.JMSException; +import jakarta.jms.Message; +import jakarta.jms.MessageListener; +import jakarta.jms.TextMessage; @Slf4j @RequiredArgsConstructor diff --git a/services/ehr-transfer-service/src/main/resources/logback.xml b/services/ehr-transfer-service/src/main/resources/logback.xml index 8fdcadae..155c9346 100644 --- a/services/ehr-transfer-service/src/main/resources/logback.xml +++ b/services/ehr-transfer-service/src/main/resources/logback.xml @@ -1,6 +1,7 @@ - + + diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/TracerTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/TracerTest.java index 3e007166..1d6cb1b4 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/TracerTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/TracerTest.java @@ -7,7 +7,7 @@ import uk.nhs.prm.repo.ehrtransferservice.logging.UpdateableTraceContext; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; -import javax.jms.JMSException; +import jakarta.jms.JMSException; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/UpdateableTraceContextTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/UpdateableTraceContextTest.java index 82c73abf..2ad52148 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/UpdateableTraceContextTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/config/UpdateableTraceContextTest.java @@ -5,7 +5,7 @@ import org.slf4j.MDC; import uk.nhs.prm.repo.ehrtransferservice.logging.UpdateableTraceContext; -import javax.jms.JMSException; +import jakarta.jms.JMSException; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcherTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcherTest.java index 4e079e6d..36c092c2 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcherTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/parsers/S3ExtendedMessageFetcherTest.java @@ -6,6 +6,7 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -25,7 +26,6 @@ import java.io.IOException; import java.io.InputStream; -import static org.junit.Assert.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -99,8 +99,8 @@ private S3PointerMessage getStaticS3PointerMessage() { private void mockS3GetObjectResponseToReturnContentFrom(String resourceFileName) { when(s3Client.getObject(any(GetObjectRequest.class))).then(invocation -> { GetObjectRequest getObjectRequest = invocation.getArgument(0); - assertEquals("s3-bucket-name", getObjectRequest.bucket()); - assertEquals("s3-key-value", getObjectRequest.key()); + Assertions.assertEquals("s3-bucket-name", getObjectRequest.bucket()); + Assertions.assertEquals("s3-key-value", getObjectRequest.key()); return new ResponseInputStream<>(GetObjectResponse.builder().build(), AbortableInputStream.create(readResourceFile(resourceFileName))); }); diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListenerTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListenerTest.java index 7e8dc4e2..6f80c1f9 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListenerTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/repo_incoming/RepoIncomingEventListenerTest.java @@ -20,7 +20,7 @@ import uk.nhs.prm.repo.ehrtransferservice.exceptions.timeout.TimeoutExceededException; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; -import javax.jms.Message; +import jakarta.jms.Message; import java.util.UUID; import java.util.stream.Stream; diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/PresignedUrlTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/PresignedUrlTest.java index fc7cd078..6c2f8414 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/PresignedUrlTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/PresignedUrlTest.java @@ -1,12 +1,9 @@ package uk.nhs.prm.repo.ehrtransferservice.services; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; -import com.github.tomakehurst.wiremock.WireMockServer; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import uk.nhs.prm.repo.ehrtransferservice.gp2gp_message_models.ParsedMessage; import java.io.IOException; @@ -14,58 +11,44 @@ import java.net.URISyntaxException; import java.net.URL; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.put; import static com.github.tomakehurst.wiremock.client.WireMock.putRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertThrows; @Tag("unit") class PresignedUrlTest { - WireMockServer wireMockServer; - - @BeforeEach - public void setUp() { - wireMockServer = initializeWebServer(); - } - - @AfterEach - public void tearDown() { - wireMockServer.resetAll(); - wireMockServer.stop(); - } - - private WireMockServer initializeWebServer() { - final WireMockServer wireMockServer = new WireMockServer(8080); - wireMockServer.start(); - return wireMockServer; - } + @RegisterExtension + static WireMockExtension wireMock = WireMockExtension.newInstance() + .options(wireMockConfig().dynamicPort()) + .build(); @Test void shouldUploadMessageToS3() throws IOException, URISyntaxException, InterruptedException { - URL url = new URL(wireMockServer.baseUrl()); + URL url = new URL(wireMock.baseUrl()); String messageBody = "test"; ParsedMessage parsedMessage = new ParsedMessage(null, null, messageBody); - wireMockServer.stubFor(put(urlEqualTo("/")).willReturn(aResponse().withStatus(200))); + wireMock.stubFor(put(urlEqualTo("/")).willReturn(aResponse().withStatus(200))); PresignedUrl presignedUrl = new PresignedUrl(url); presignedUrl.uploadMessage(parsedMessage); - verify(putRequestedFor(urlMatching("/")) + wireMock.verify(putRequestedFor(urlMatching("/")) .withRequestBody(equalTo(messageBody))); } @Test void shouldThrowErrorWhenCannotUploadMessageToS3() throws MalformedURLException { - URL url = new URL(wireMockServer.baseUrl()); + URL url = new URL(wireMock.baseUrl()); String messageBody = "test"; ParsedMessage parsedMessage = new ParsedMessage(null, null, messageBody); - wireMockServer.stubFor(put(urlEqualTo("/")).willReturn(aResponse().withStatus(503))); + wireMock.stubFor(put(urlEqualTo("/")).willReturn(aResponse().withStatus(503))); PresignedUrl presignedUrl = new PresignedUrl(url); Exception expected = assertThrows(RuntimeException.class, () -> @@ -73,7 +56,7 @@ void shouldThrowErrorWhenCannotUploadMessageToS3() throws MalformedURLException ); assertThat(expected, notNullValue()); - verify(putRequestedFor(urlMatching("/")) + wireMock.verify(putRequestedFor(urlMatching("/")) .withRequestBody(equalTo(messageBody))); } } diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/ehr_repo/EhrRepoClientTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/ehr_repo/EhrRepoClientTest.java index 850910d2..9bcf27ed 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/ehr_repo/EhrRepoClientTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/ehr_repo/EhrRepoClientTest.java @@ -1,11 +1,11 @@ package uk.nhs.prm.repo.ehrtransferservice.services.ehr_repo; -import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import org.hamcrest.Matchers; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; import uk.nhs.prm.repo.ehrtransferservice.exceptions.DuplicateMessageException; import uk.nhs.prm.repo.ehrtransferservice.exceptions.HttpException; @@ -38,7 +38,7 @@ import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -48,27 +48,17 @@ @Tag("unit") class EhrRepoClientTest { - WireMockServer wireMockServer; - static Tracer tracer = mock(Tracer.class); static UUID traceId = UUID.randomUUID(); + @RegisterExtension + static WireMockExtension wireMock = WireMockExtension.newInstance() + .options(wireMockConfig().dynamicPort()) + .build(); + @BeforeEach public void setUp() { when(tracer.getTraceId()).thenReturn(String.valueOf(traceId)); - wireMockServer = initializeWebServer(); - } - - @AfterEach - public void tearDown() throws Exception { - wireMockServer.resetAll(); - wireMockServer.stop(); - } - - private WireMockServer initializeWebServer() { - final WireMockServer wireMockServer = new WireMockServer(8080); - wireMockServer.start(); - return wireMockServer; } @Test @@ -77,7 +67,7 @@ void shouldFetchStorageUrlFromEhrRepo() throws Exception { UUID messageId = UUID.randomUUID(); String presignedUrl = "https://fake-presigned-url"; - wireMockServer.stubFor(get(urlEqualTo("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) + wireMock.stubFor(get(urlEqualTo("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) .withHeader("Authorization", equalTo("secret")) .withHeader("traceId", equalTo(String.valueOf(traceId))) .willReturn(aResponse() @@ -85,10 +75,10 @@ void shouldFetchStorageUrlFromEhrRepo() throws Exception { .withBody(presignedUrl) .withHeader("Content-Type", "application/json"))); - EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMockServer.baseUrl(), "secret", tracer); + EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMock.baseUrl(), "secret", tracer); PresignedUrl response = ehrRepoClient.fetchStorageUrl(conversationId, messageId); - verify(getRequestedFor(urlMatching("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) + wireMock.verify(getRequestedFor(urlMatching("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) .withHeader("Content-Type", matching("application/json")) .withHeader("Authorization", matching("secret")) .withHeader("traceId", matching(String.valueOf(traceId)))); @@ -101,19 +91,19 @@ void shouldThrowErrorWhenCannotFetchStorageUrlFromEhrRepo() throws MalformedURLE UUID conversationId = UUID.randomUUID(); UUID messageId = UUID.randomUUID(); - wireMockServer.stubFor(get(urlEqualTo("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) + wireMock.stubFor(get(urlEqualTo("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) .withHeader("Authorization", equalTo("secret")) .willReturn(aResponse() .withStatus(503) .withHeader("Content-Type", "application/json"))); - EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMockServer.baseUrl(), "secret", tracer); + EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMock.baseUrl(), "secret", tracer); Exception expected = assertThrows(Exception.class, () -> ehrRepoClient.fetchStorageUrl(conversationId, messageId) ); assertThat(expected, notNullValue()); - verify(getRequestedFor(urlMatching("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) + wireMock.verify(getRequestedFor(urlMatching("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) .withHeader("Content-Type", matching("application/json")) .withHeader("Authorization", matching("secret"))); } @@ -124,13 +114,13 @@ void shouldThrowDuplicateMessageExceptionWhenReceiving409() throws MalformedURLE UUID messageId = UUID.randomUUID(); String presignedUrl = "https://fake-presigned-url"; - wireMockServer.stubFor(get(urlEqualTo("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) + wireMock.stubFor(get(urlEqualTo("/messages/" + conversationId.toString().toUpperCase() + "/" + messageId.toString().toUpperCase())) .withHeader("Authorization", equalTo("secret")) .willReturn(aResponse() .withStatus(409) .withHeader("Content-Type", "application/json"))); - EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMockServer.baseUrl(), "secret", tracer); + EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMock.baseUrl(), "secret", tracer); Exception expected = assertThrows(DuplicateMessageException.class, () -> ehrRepoClient.fetchStorageUrl(conversationId, messageId) ); @@ -142,7 +132,7 @@ void shouldThrowErrorWhenCannotStoreMessageInEhrRepo() throws MalformedURLExcept UUID conversationId = UUID.randomUUID(); UUID messageId = UUID.randomUUID(); - wireMockServer.stubFor(post(urlEqualTo("/messages")) + wireMock.stubFor(post(urlEqualTo("/messages")) .withHeader("Authorization", equalTo("secret")) .willReturn(aResponse() .withStatus(503) @@ -155,7 +145,7 @@ void shouldThrowErrorWhenCannotStoreMessageInEhrRepo() throws MalformedURLExcept when(mockParsedMessage.getInteractionId()).thenReturn("RCMR_IN030000UK06"); when(mockParsedMessage.getFragmentMessageIds()).thenReturn(Collections.emptyList()); - EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMockServer.baseUrl(), "secret", tracer); + EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMock.baseUrl(), "secret", tracer); Exception expected = assertThrows(HttpException.class, () -> ehrRepoClient.confirmMessageStored(mockParsedMessage) ); @@ -176,14 +166,14 @@ void shouldConfirmMessageStoredInEhrRepo() throws Exception { //TODO: Refactor the below json body String responseBody = "{\"healthRecordStatus\":\"complete\"}"; // Mock request - wireMockServer.stubFor(post(urlEqualTo("/messages")) + wireMock.stubFor(post(urlEqualTo("/messages")) .withHeader("Authorization", equalTo("secret")) .withHeader("traceId", equalTo(String.valueOf(traceId))) .willReturn(aResponse() .withStatus(201) .withHeader("Content-Type", "application/json").withBody(responseBody))); - EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMockServer.baseUrl(), "secret", tracer); + EhrRepoClient ehrRepoClient = new EhrRepoClient(wireMock.baseUrl(), "secret", tracer); // Create parsed message to store SOAPEnvelope envelope = getSoapEnvelope(conversationId, messageId, fragmentId, interactionId); @@ -193,7 +183,7 @@ void shouldConfirmMessageStoredInEhrRepo() throws Exception { // Store var response = ehrRepoClient.confirmMessageStored(parsedMessage); - verify(postRequestedFor(urlMatching("/messages")) + wireMock.verify(postRequestedFor(urlMatching("/messages")) .withRequestBody(equalToJson((requestBody))) .withHeader("Content-Type", matching("application/json")) .withHeader("Authorization", matching("secret")) diff --git a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/gp2gp_messenger/Gp2gpMessengerClientTest.java b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/gp2gp_messenger/Gp2gpMessengerClientTest.java index 8b085791..74841d8a 100644 --- a/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/gp2gp_messenger/Gp2gpMessengerClientTest.java +++ b/services/ehr-transfer-service/src/test/java/uk/nhs/prm/repo/ehrtransferservice/services/gp2gp_messenger/Gp2gpMessengerClientTest.java @@ -1,12 +1,10 @@ package uk.nhs.prm.repo.ehrtransferservice.services.gp2gp_messenger; -import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import com.google.gson.Gson; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import org.mockito.MockitoAnnotations; +import org.junit.jupiter.api.extension.RegisterExtension; import uk.nhs.prm.repo.ehrtransferservice.logging.Tracer; import uk.nhs.prm.repo.ehrtransferservice.exceptions.HttpException; import uk.nhs.prm.repo.ehrtransferservice.gp2gp_message_models.Gp2gpMessengerContinueMessageRequestBody; @@ -25,35 +23,17 @@ import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; -import static com.github.tomakehurst.wiremock.client.WireMock.verify; +import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.junit.jupiter.api.Assertions.assertThrows; @Tag("unit") class Gp2gpMessengerClientTest { - WireMockServer wireMockServer; - - private AutoCloseable closeable; - Tracer tracer = new Tracer(); - @BeforeEach - public void setUp() { - closeable = MockitoAnnotations.openMocks(this); - wireMockServer = initializeWebServer(); - } - - @AfterEach - public void tearDown() throws Exception { - closeable.close(); - wireMockServer.resetAll(); - wireMockServer.stop(); - } - - private WireMockServer initializeWebServer() { - final WireMockServer wireMockServer = new WireMockServer(8080); - wireMockServer.start(); - return wireMockServer; - } + @RegisterExtension + static WireMockExtension wireMock = WireMockExtension.newInstance() + .options(wireMockConfig().dynamicPort()) + .build(); @Test void shouldCallGP2GpMessengerEHRRequest() throws IOException, URISyntaxException, InterruptedException, HttpException { @@ -62,7 +42,7 @@ void shouldCallGP2GpMessengerEHRRequest() throws IOException, URISyntaxException String jsonPayloadString = new Gson().toJson(requestBody); - wireMockServer.stubFor(post(urlEqualTo("/health-record-requests/1234567890")) + wireMock.stubFor(post(urlEqualTo("/health-record-requests/1234567890")) .withHeader("Authorization", matching("secret")) .willReturn(aResponse() .withStatus(204) @@ -71,10 +51,10 @@ void shouldCallGP2GpMessengerEHRRequest() throws IOException, URISyntaxException tracer.directlyUpdateTraceIdButNotConversationId("some-trace-id"); - Gp2gpMessengerClient gp2gpMessengerClient = new Gp2gpMessengerClient(wireMockServer.baseUrl(), "secret", tracer); + Gp2gpMessengerClient gp2gpMessengerClient = new Gp2gpMessengerClient(wireMock.baseUrl(), "secret", tracer); gp2gpMessengerClient.sendGp2gpMessengerEhrRequest("1234567890", requestBody); - verify(postRequestedFor(urlMatching("/health-record-requests/1234567890")) + wireMock.verify(postRequestedFor(urlMatching("/health-record-requests/1234567890")) .withRequestBody(equalToJson((jsonPayloadString))) .withHeader("Content-Type", matching("application/json")) .withHeader("Authorization", matching("secret"))); @@ -88,7 +68,7 @@ void shouldCallGP2GpMessengerPositiveRequest() throws IOException, URISyntaxExce String jsonPayloadString = new Gson().toJson(requestBody); - wireMockServer.stubFor(post(urlEqualTo("/health-record-requests/1234567890/acknowledgement")) + wireMock.stubFor(post(urlEqualTo("/health-record-requests/1234567890/acknowledgement")) .withHeader("Authorization", matching("secret")) .withRequestBody(equalTo(jsonPayloadString)) .willReturn(aResponse() @@ -97,10 +77,10 @@ void shouldCallGP2GpMessengerPositiveRequest() throws IOException, URISyntaxExce tracer.directlyUpdateTraceIdButNotConversationId("some-trace-id"); - Gp2gpMessengerClient gp2gpMessengerClient = new Gp2gpMessengerClient(wireMockServer.baseUrl(), "secret", tracer); + Gp2gpMessengerClient gp2gpMessengerClient = new Gp2gpMessengerClient(wireMock.baseUrl(), "secret", tracer); gp2gpMessengerClient.sendGp2gpMessengerAcknowledgement("1234567890", requestBody); - verify(postRequestedFor(urlMatching("/health-record-requests/1234567890/acknowledgement")) + wireMock.verify(postRequestedFor(urlMatching("/health-record-requests/1234567890/acknowledgement")) .withRequestBody(equalToJson((jsonPayloadString))) .withHeader("Content-Type", matching("application/json")) .withHeader("Authorization", matching("secret"))); @@ -112,7 +92,7 @@ void shouldThrowHTTPExceptionWhenWeGotAnyStatusCodeButNot204ForPositiveAcknowled String jsonPayloadString = new Gson().toJson(requestBody); - wireMockServer.stubFor(post(urlEqualTo("/health-record-requests/1234567890/acknowledgement")) + wireMock.stubFor(post(urlEqualTo("/health-record-requests/1234567890/acknowledgement")) .withHeader("Authorization", matching("secret")) .withRequestBody(equalTo(jsonPayloadString)) .willReturn(aResponse() @@ -121,7 +101,7 @@ void shouldThrowHTTPExceptionWhenWeGotAnyStatusCodeButNot204ForPositiveAcknowled tracer.directlyUpdateTraceIdButNotConversationId("some-trace-id"); - Gp2gpMessengerClient gp2gpMessengerClient = new Gp2gpMessengerClient(wireMockServer.baseUrl(), "secret", tracer); + Gp2gpMessengerClient gp2gpMessengerClient = new Gp2gpMessengerClient(wireMock.baseUrl(), "secret", tracer); assertThrows(HttpException.class, () -> gp2gpMessengerClient.sendGp2gpMessengerAcknowledgement("1234567890", requestBody)); } @@ -131,7 +111,7 @@ void shouldCallRequestBuilderWithExpectedRequestBody() throws IOException, HttpE UUID ehrExtractMessageId = UUID.randomUUID(); Gp2gpMessengerContinueMessageRequestBody continueMessageRequestBody = new Gp2gpMessengerContinueMessageRequestBody(conversationId, "gp-ods-code", ehrExtractMessageId); var jsonPayloadString = new Gson().toJson(continueMessageRequestBody); - wireMockServer.stubFor(post(urlEqualTo("/health-record-requests/continue-message")) + wireMock.stubFor(post(urlEqualTo("/health-record-requests/continue-message")) .withHeader("Authorization", matching("secret")) .willReturn(aResponse() .withStatus(204) @@ -140,10 +120,10 @@ void shouldCallRequestBuilderWithExpectedRequestBody() throws IOException, HttpE tracer.directlyUpdateTraceIdButNotConversationId("some-trace-id"); - var gp2gpMessengerClient = new Gp2gpMessengerClient(wireMockServer.baseUrl(), "secret", tracer); + var gp2gpMessengerClient = new Gp2gpMessengerClient(wireMock.baseUrl(), "secret", tracer); gp2gpMessengerClient.sendContinueMessage(continueMessageRequestBody); - verify(postRequestedFor(urlMatching("/health-record-requests/continue-message")) + wireMock.verify(postRequestedFor(urlMatching("/health-record-requests/continue-message")) .withRequestBody(equalToJson((jsonPayloadString))) .withHeader("Content-Type", matching("application/json")) .withHeader("Authorization", matching("secret"))); @@ -155,7 +135,7 @@ void shouldThrowAnExceptionWhenResponseStatusCodeIsNot204() throws IOException { UUID ehrExtractMessageId = UUID.randomUUID(); Gp2gpMessengerContinueMessageRequestBody continueMessageRequestBody = new Gp2gpMessengerContinueMessageRequestBody(conversationId, "gp-ods-code", ehrExtractMessageId); var jsonPayloadString = new Gson().toJson(continueMessageRequestBody); - wireMockServer.stubFor(post(urlEqualTo("/health-record-requests/continue-message")) + wireMock.stubFor(post(urlEqualTo("/health-record-requests/continue-message")) .withHeader("Authorization", matching("secret")) .willReturn(aResponse() .withStatus(500) @@ -164,7 +144,7 @@ void shouldThrowAnExceptionWhenResponseStatusCodeIsNot204() throws IOException { tracer.directlyUpdateTraceIdButNotConversationId("some-trace-id"); - var gp2gpMessengerClient = new Gp2gpMessengerClient(wireMockServer.baseUrl(), "secret", tracer); + var gp2gpMessengerClient = new Gp2gpMessengerClient(wireMock.baseUrl(), "secret", tracer); assertThrows(HttpException.class, () -> gp2gpMessengerClient.sendContinueMessage(continueMessageRequestBody)); } } diff --git a/services/ehr-transfer-service/tasks b/services/ehr-transfer-service/tasks index 7b93c610..f4956f34 100755 --- a/services/ehr-transfer-service/tasks +++ b/services/ehr-transfer-service/tasks @@ -7,44 +7,7 @@ set -Eeo pipefail ########################### AWS_DEFAULT_REGION=eu-west-2 -IMAGE_REPO_NAME=deductions/ehr-transfer-service export NHS_SERVICE=ehr-transfer-service -AWS_HELPERS_VERSION=0.2.27 -echo "AWS helper scripts version: $AWS_HELPERS_VERSION" - -########################### -# Shared utils # -########################### - -function download_util() { - local UTIL_VERSION=$1 - local UTIL_FILENAME=$2 - - local UTIL_FILEPATH="utils/$UTIL_VERSION/$UTIL_FILENAME" - - mkdir -p "utils/$UTIL_VERSION" - if [[ ! -f $UTIL_FILEPATH ]];then - wget --quiet -O $UTIL_FILEPATH https://github.com/nhsconnect/prm-deductions-support-infra/releases/download/${UTIL_VERSION}/${UTIL_FILENAME} - fi - chmod +x $UTIL_FILEPATH - - echo "$UTIL_FILEPATH" -} - -function fetch_redaction_utils() { - download_util $AWS_HELPERS_VERSION run-with-redaction.sh - download_util $AWS_HELPERS_VERSION redactor -} - -# Do not change the file name as the aws helper scripts depend on it -AWS_HELPERS_FILE="utils/$AWS_HELPERS_VERSION/aws-helpers" - -mkdir -p "utils/$AWS_HELPERS_VERSION" -if [[ ! -f $AWS_HELPERS_FILE ]];then - wget --quiet -O $AWS_HELPERS_FILE https://github.com/nhsconnect/prm-deductions-support-infra/releases/download/${AWS_HELPERS_VERSION}/aws-helpers -fi -chmod +x $AWS_HELPERS_FILE -source $AWS_HELPERS_FILE #################################### # Instance (Environment) Variables # @@ -57,14 +20,6 @@ function check_env { fi } -function set_image_tag() { - if [[ -z "${GO_DEPENDENCY_LABEL_APP}" ]]; then - export IMAGE_TAG=${GO_PIPELINE_LABEL:-$(git rev-parse HEAD | cut -c 1-8)} - else - export IMAGE_TAG=${GO_DEPENDENCY_LABEL_APP} - fi -} - function configure_local_envs { export EHR_TRANSFER_SERVICE_MHS_QUEUE_URL_1="tcp://127.0.0.1:61616" export EHR_TRANSFER_SERVICE_MHS_QUEUE_URL_2="tcp://127.0.0.1:61617" @@ -86,25 +41,29 @@ function configure_sonar_environment_variable { export SONAR_TOKEN=$(_get_aws_ssm_secret "/repo/dev/output/ehr-transfer-service/sonar_token") } +############# +# FUNCTIONS # +############# +function start_localstack { + echo "################################" + echo "##### Starting Localstack ######" + echo "################################" + docker-compose -f docker-compose.localstack-local.yaml up -d +} + +function stop_localstack { + echo "################################" + echo "##### Stopping Localstack ######" + echo "################################" + docker-compose -f docker-compose.localstack-local.yaml down +} + ########### ## TASKS ## ########### command="$1" case "${command}" in - assemble_docker) - configure_local_envs - ./tasks assemble - configure_docker_repository_uri - fetch_redaction_utils - build_docker_image - echo "Pushing the Docker image... $REPOSITORY_URI:latest $REPOSITORY_URI:$IMAGE_TAG" - docker push $REPOSITORY_URI:$IMAGE_TAG - ;; - show_image_tag) - set_image_tag - echo $IMAGE_TAG - ;; build) rm -rf build/ ./gradlew build -x integration @@ -118,42 +77,37 @@ case "${command}" in ./gradlew test ;; test_integration) -# ensure_prev_build_images_are_stopped configure_local_envs + start_localstack ./gradlew --info integration - ;; - test_coverage) - ensure_prev_build_images_are_stopped - configure_local_envs - ./gradlew jacocoTestCoverageVerification - ;; - fetch_utils) - fetch_redaction_utils + stop_localstack ;; code_quality) - ensure_prev_build_images_are_stopped configure_local_envs + start_localstack ./gradlew check -x test -x integration + stop_localstack ;; test_all) configure_local_envs + start_localstack ./gradlew test integration jacocoTestCoverageVerification check + stop_localstack ;; run_sonar) - ensure_prev_build_images_are_stopped _assume_environment_role $NHS_ENVIRONMENT configure_local_envs configure_sonar_environment_variable + start_localstack ./gradlew build sonar --info + stop_localstack ;; - run_localstack_local) + start_localstack) configure_local_envs - docker compose -f docker-compose.localstack-local.yaml up -d + start_localstack ;; - promote_docker_image) - check_env - set_image_tag - promote_docker_image "$IMAGE_REPO_NAME:$IMAGE_TAG" "$NHS_ENVIRONMENT" + stop_localstack) + stop_localstack ;; wait_ecs) check_env diff --git a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisherTest.java b/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisherTest.java deleted file mode 100644 index b58e9689..00000000 --- a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisherTest.java +++ /dev/null @@ -1,113 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.jetbrains.annotations.NotNull; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; -import software.amazon.awssdk.services.cloudwatch.model.*; -import uk.nhs.prm.deductions.nemseventprocessor.config.SnsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.config.SqsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.nemsevents.LocalStackAwsConfig; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; - -import static java.util.stream.Collectors.toList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - -@SpringBootTest() -@ActiveProfiles("test") -@SpringJUnitConfig(ScheduledTestConfig.class) -@TestPropertySource(properties = {"environment = local"}) -@ContextConfiguration(classes = { LocalStackAwsConfig.class}) -@ExtendWith(MockitoExtension.class) -class MetricPublisherTest { - - @Autowired - private MetricPublisher publisher; - - @Autowired - private CloudWatchClient cloudWatchClient; - - static final double HEALTHY_HEALTH_VALUE = 1.0; - - @Test - void shouldPutHealthMetricDataIntoCloudWatch() { - // TODO investigate why this test intermittently fails - System.out.println("If this test fails on the pipeline, rerun it. It fails intermittently."); - - publisher.publishMetric("Health", HEALTHY_HEALTH_VALUE); - - List metrics = fetchMetricsMatching("NemsEventProcessor", "Health"); - assertThat(metrics).isNotEmpty(); - - final MetricDataResult[] metricData = new MetricDataResult[1]; - await().atMost(60, TimeUnit.SECONDS).untilAsserted(() -> { - metricData[0] = fetchRecentMetricData(2, getMetricWhere(metrics, metricHasDimension("Environment", "local"))); - assertThat(metricData[0].values()).isNotEmpty(); - }); - - assertThat(metricData[0].values()).isNotEmpty(); - assertThat(metricData[0].values().get(0)).isEqualTo(HEALTHY_HEALTH_VALUE); - } - - @NotNull - private Predicate metricHasDimension(String name, String value) { - return metric -> metric.dimensions().stream().anyMatch(dimension -> - dimension.name().equals(name) && dimension.value().equals(value)); - } - - private Metric getMetricWhere(List metrics, Predicate metricPredicate) { - List filteredMetrics = metrics.stream().filter(metricPredicate).collect(toList()); - return filteredMetrics.get(0); - } - private List fetchMetricsMatching(String namespace, String metricName) { - ListMetricsRequest request = ListMetricsRequest.builder() - .namespace(namespace) - .metricName(metricName) - .recentlyActive(RecentlyActive.PT3_H) - .build(); - - ListMetricsResponse listMetricsResponse = cloudWatchClient.listMetrics(request); - return listMetricsResponse.metrics(); - } - - private MetricDataResult fetchRecentMetricData(int minutesOfRecency, Metric metric) { - MetricDataQuery dataQuery = MetricDataQuery.builder() - .id("health_test_query") - .metricStat(MetricStat.builder() - .metric(metric) - .period(1) - .stat("Minimum") - .build()) - .returnData(true) - .build(); - GetMetricDataRequest request = GetMetricDataRequest.builder() - .startTime(Instant.now().minusSeconds(minutesOfRecency * 60).truncatedTo(ChronoUnit.MINUTES)) - .endTime(Instant.now().truncatedTo(ChronoUnit.MINUTES)) - .metricDataQueries(dataQuery) - .build(); - - List metricDataResults = cloudWatchClient.getMetricData(request).metricDataResults(); - System.out.println("metric data results size: " + metricDataResults.size()); - assertThat(metricDataResults.size()).isEqualTo(1); - - MetricDataResult metricDataResult = metricDataResults.get(0); - assertThat(metricDataResult.statusCode()).isEqualTo(StatusCode.COMPLETE); - System.out.println("metric data result status: " + metricDataResult.statusCodeAsString()); - System.out.println("metric data result hasValues: " + metricDataResult.hasValues()); - System.out.println("metric data result hasTimestamps: " + metricDataResult.hasTimestamps()); - return metricDataResult; - } -} diff --git a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/ScheduledTestConfig.java b/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/ScheduledTestConfig.java deleted file mode 100644 index 9e358d0e..00000000 --- a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/ScheduledTestConfig.java +++ /dev/null @@ -1,13 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.FilterType; -import org.springframework.scheduling.annotation.EnableScheduling; - -@Configuration -@EnableScheduling -@ComponentScan(excludeFilters = -@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = HealthMetricPublisher.class)) -public class ScheduledTestConfig { -} diff --git a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/SqsHealthProbeTest.java b/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/SqsHealthProbeTest.java deleted file mode 100644 index cc83ec1a..00000000 --- a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/SqsHealthProbeTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import software.amazon.awssdk.services.sqs.SqsClient; -import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; -import software.amazon.awssdk.services.sqs.model.CreateQueueResponse; -import software.amazon.awssdk.services.sqs.model.DeleteQueueRequest; -import software.amazon.awssdk.services.sqs.model.QueueAttributeName; -import uk.nhs.prm.deductions.nemseventprocessor.config.SnsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.config.SqsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.SqsHealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.nemsevents.LocalStackAwsConfig; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@SpringBootTest() -@ActiveProfiles("test") -@SpringJUnitConfig(ScheduledTestConfig.class) -@TestPropertySource(properties = {"environment = local"}) -@ContextConfiguration(classes = {LocalStackAwsConfig.class}) -@ExtendWith(MockitoExtension.class) -public class SqsHealthProbeTest { - - @Autowired - private SqsClient sqsClient; - - static CreateQueueResponse queue; - - static final String queueName = "integration-test-sqs-health-probe"; - - @BeforeAll - public static void setUpQueue(@Autowired SqsClient sqsClient) { - Map attributes = new HashMap<>(); - attributes.put(QueueAttributeName.KMS_MASTER_KEY_ID, UUID.randomUUID().toString()); - - queue = sqsClient.createQueue(CreateQueueRequest.builder().queueName(queueName).attributes(attributes).build()); - } - - @Test - void shouldReturnUnhealthyIfCannotQuerySqsQueue() { - AppConfig config = new AppConfig("int-test", "non-existent-queue", "non-existent-sns-topic", "suspension-non-existent"); - SqsHealthProbe sqsHealthProbe = new SqsHealthProbe(config, sqsClient); - - assertFalse(sqsHealthProbe.isHealthy()); - } - - @Test - void shouldReturnHealthyIfCanQuerySqsQueue() { - AppConfig config = new AppConfig("int-test", queueName,"non-existent-sns-topic", "suspension-non-existent"); - SqsHealthProbe sqsHealthProbe = new SqsHealthProbe(config, sqsClient); - - assertTrue(sqsHealthProbe.isHealthy()); - } - - @AfterAll - static void tearDownQueue(@Autowired SqsClient sqsClient) { - sqsClient.deleteQueue(DeleteQueueRequest.builder().queueUrl(queue.queueUrl()).build()); - } -} diff --git a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/SuspensionsSnsHealthProbeTest.java b/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/SuspensionsSnsHealthProbeTest.java deleted file mode 100644 index 5c51b570..00000000 --- a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/SuspensionsSnsHealthProbeTest.java +++ /dev/null @@ -1,71 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import software.amazon.awssdk.services.sns.SnsClient; -import software.amazon.awssdk.services.sns.model.CreateTopicRequest; -import software.amazon.awssdk.services.sns.model.CreateTopicResponse; -import software.amazon.awssdk.services.sns.model.DeleteTopicRequest; -import uk.nhs.prm.deductions.nemseventprocessor.config.SnsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.config.SqsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.HealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.SuspensionsSnsHealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.nemsevents.LocalStackAwsConfig; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@SpringBootTest() -@ActiveProfiles("test") -@SpringJUnitConfig(ScheduledTestConfig.class) -@TestPropertySource(properties = {"environment = local"}) -@ContextConfiguration(classes = {LocalStackAwsConfig.class}) -@ExtendWith(MockitoExtension.class) -class SuspensionsSnsHealthProbeTest { - - @Autowired - private SnsClient snsClient; - static CreateTopicResponse topic; - - @BeforeAll - static void setUpTopic(@Autowired SnsClient snsClient) { - Map attributes = new HashMap<>(); - attributes.put("KmsMasterKeyId", "aws/sns"); - - String snsTopicName = "integration-test-suspensions-health-probe"; - topic = snsClient.createTopic(CreateTopicRequest.builder().name(snsTopicName).attributes(attributes).build()); - } - - @Test - void shouldReturnUnhealthyIfCannotQuerySnsTopic() { - AppConfig config = new AppConfig("int-test", "non-existent-queue", "non-existent-sns-topic", "suspension-non-existent"); - HealthProbe suspensionsSnsHealthProbe = new SuspensionsSnsHealthProbe(config, snsClient); - - assertFalse(suspensionsSnsHealthProbe.isHealthy()); - } - - @Test - void shouldReturnHealthyIfCanQuerySnsTopic() { - AppConfig config = new AppConfig("int-test", "non-existent-queue", "unhandled-non-existent", topic.topicArn()); - HealthProbe suspensionsSnsHealthProbe = new SuspensionsSnsHealthProbe(config, snsClient); - - assertTrue(suspensionsSnsHealthProbe.isHealthy()); - } - - @AfterAll - static void tearDownTopic(@Autowired SnsClient snsClient) { - snsClient.deleteTopic(DeleteTopicRequest.builder().topicArn(topic.topicArn()).build()); - } -} \ No newline at end of file diff --git a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/UnhandledSnsHealthProbeTest.java b/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/UnhandledSnsHealthProbeTest.java deleted file mode 100644 index 477b949d..00000000 --- a/services/nems-event-processor/src/integration/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/UnhandledSnsHealthProbeTest.java +++ /dev/null @@ -1,69 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; -import software.amazon.awssdk.services.sns.SnsClient; -import software.amazon.awssdk.services.sns.model.CreateTopicRequest; -import software.amazon.awssdk.services.sns.model.CreateTopicResponse; -import software.amazon.awssdk.services.sns.model.DeleteTopicRequest; -import uk.nhs.prm.deductions.nemseventprocessor.config.SnsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.config.SqsClientSpringConfiguration; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.UnhandledSnsHealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.nemsevents.LocalStackAwsConfig; - -import java.util.HashMap; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -@SpringBootTest() -@ActiveProfiles("test") -@SpringJUnitConfig(ScheduledTestConfig.class) -@TestPropertySource(properties = {"environment = local"}) -@ContextConfiguration(classes = {LocalStackAwsConfig.class}) -@ExtendWith(MockitoExtension.class) -class UnhandledSnsHealthProbeTest { - @Autowired - private SnsClient snsClient; - static CreateTopicResponse topic; - - @BeforeAll - static void setUpTopic(@Autowired SnsClient snsClient) { - Map attributes = new HashMap<>(); - attributes.put("KmsMasterKeyId", "aws/sns"); - - String snsTopicName = "integration-test-unhandled-health-probe"; - topic = snsClient.createTopic(CreateTopicRequest.builder().name(snsTopicName).attributes(attributes).build()); - } - - @Test - void shouldReturnUnhealthyIfCannotQuerySnsTopic() { - AppConfig config = new AppConfig("int-test", "non-existent-queue", "non-existent-sns-topic", "suspension-non-existent"); - UnhandledSnsHealthProbe unhandledSnsHealthProbe = new UnhandledSnsHealthProbe(config, snsClient); - - assertFalse(unhandledSnsHealthProbe.isHealthy()); - } - - @Test - void shouldReturnHealthyIfCanQuerySnsTopic() { - AppConfig config = new AppConfig("int-test", "non-existent-queue", topic.topicArn(), "suspension-non-existent"); - UnhandledSnsHealthProbe unhandledSnsHealthProbe = new UnhandledSnsHealthProbe(config, snsClient); - - assertTrue(unhandledSnsHealthProbe.isHealthy()); - } - - @AfterAll - static void tearDownTopic(@Autowired SnsClient snsClient) { - snsClient.deleteTopic(DeleteTopicRequest.builder().topicArn(topic.topicArn()).build()); - } -} \ No newline at end of file diff --git a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisher.java b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisher.java index 7b10208b..a51ab7d4 100644 --- a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisher.java +++ b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisher.java @@ -8,6 +8,13 @@ import java.util.List; +/** + * TODO - THIS SHOULD BE REMOVED + * This class is only used to publish health check metrics for the service's connection to CloudWatch. + * We think this is redundant, however, when we've tried to remove health checks from other services, pipelines have + * failed. The health checks are quite deeply rooted in the codebase. For now, we're leaving this in place. + */ + @Component @Slf4j public class HealthMetricPublisher { diff --git a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisher.java b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisher.java index 5d4c80de..cf59c6f3 100644 --- a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisher.java +++ b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisher.java @@ -14,6 +14,13 @@ import java.time.temporal.ChronoUnit; import java.util.ArrayList; +/** + * TODO - THIS SHOULD BE REMOVED + * This class is only used to publish health check metrics for the service's connection to CloudWatch. + * We think this is redundant, however, when we've tried to remove health checks from other services, pipelines have + * failed. The health checks are quite deeply rooted in the codebase. For now, we're leaving this in place. + */ + @Component public class MetricPublisher { public CloudWatchClient cloudWatchClient; diff --git a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SqsHealthProbe.java b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SqsHealthProbe.java index a505d81b..816ddfee 100644 --- a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SqsHealthProbe.java +++ b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SqsHealthProbe.java @@ -8,6 +8,13 @@ import software.amazon.awssdk.services.sqs.model.GetQueueUrlRequest; import uk.nhs.prm.deductions.nemseventprocessor.metrics.AppConfig; +/** + * TODO - THIS SHOULD BE REMOVED + * This class is only used to publish health check metrics for the service's connection to SQS. + * We think this is redundant, however, when we've tried to remove health checks from other services, pipelines have + * failed. The health checks are quite deeply rooted in the codebase. For now, we're leaving this in place. + */ + @Component @Slf4j public class SqsHealthProbe implements HealthProbe { diff --git a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SuspensionsSnsHealthProbe.java b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SuspensionsSnsHealthProbe.java index 5af304d7..5898b5d3 100644 --- a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SuspensionsSnsHealthProbe.java +++ b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/SuspensionsSnsHealthProbe.java @@ -7,6 +7,13 @@ import software.amazon.awssdk.services.sns.model.GetTopicAttributesRequest; import uk.nhs.prm.deductions.nemseventprocessor.metrics.AppConfig; +/** + * TODO - THIS SHOULD BE REMOVED + * This class is only used to publish health check metrics for the service's connection to SNS. + * We think this is redundant, however, when we've tried to remove health checks from other services, pipelines have + * failed. The health checks are quite deeply rooted in the codebase. For now, we're leaving this in place. + */ + @Slf4j @Component public class SuspensionsSnsHealthProbe implements HealthProbe { diff --git a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/UnhandledSnsHealthProbe.java b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/UnhandledSnsHealthProbe.java index 0e47c77f..2d16d8be 100644 --- a/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/UnhandledSnsHealthProbe.java +++ b/services/nems-event-processor/src/main/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/healthprobes/UnhandledSnsHealthProbe.java @@ -7,6 +7,13 @@ import software.amazon.awssdk.services.sns.model.GetTopicAttributesRequest; import uk.nhs.prm.deductions.nemseventprocessor.metrics.AppConfig; +/** + * TODO - THIS SHOULD BE REMOVED + * This class is only used to publish health check metrics for the service's connection to SNS. + * We think this is redundant, however, when we've tried to remove health checks from other services, pipelines have + * failed. The health checks are quite deeply rooted in the codebase. For now, we're leaving this in place. + */ + @Slf4j @Component public class UnhandledSnsHealthProbe implements HealthProbe { diff --git a/services/nems-event-processor/src/test/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisherTest.java b/services/nems-event-processor/src/test/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisherTest.java deleted file mode 100644 index cd001bd0..00000000 --- a/services/nems-event-processor/src/test/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/HealthMetricPublisherTest.java +++ /dev/null @@ -1,58 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.HealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.SqsHealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.SuspensionsSnsHealthProbe; -import uk.nhs.prm.deductions.nemseventprocessor.metrics.healthprobes.UnhandledSnsHealthProbe; - -import java.util.ArrayList; -import java.util.List; - -import static org.mockito.Mockito.*; - -@ExtendWith(MockitoExtension.class) -class HealthMetricPublisherTest { -private MetricPublisher metricPublisher; -private SqsHealthProbe sqsHealthProbe; -private UnhandledSnsHealthProbe unhandledSnsHealthProbe; -private SuspensionsSnsHealthProbe suspensionsSnsHealthProbe; -private List probe = new ArrayList<>(); - - @BeforeEach - void setUp(){ - metricPublisher = Mockito.mock(MetricPublisher.class); - sqsHealthProbe = Mockito.mock(SqsHealthProbe.class); - unhandledSnsHealthProbe = Mockito.mock(UnhandledSnsHealthProbe.class); - suspensionsSnsHealthProbe = Mockito.mock(SuspensionsSnsHealthProbe.class); - probe.add(sqsHealthProbe); - probe.add(unhandledSnsHealthProbe); - probe.add(suspensionsSnsHealthProbe); - } - - @Test - public void shouldSetHealthMetricToZeroForUnhealthyIfAnyConnectionIsUnhealthy() { - when(sqsHealthProbe.isHealthy()).thenReturn(false); - - HealthMetricPublisher healthPublisher = new HealthMetricPublisher(metricPublisher,probe); - healthPublisher.publishHealthStatus(); - - verify(metricPublisher,times(1)).publishMetric("Health", 0.0); - } - - @Test - public void shouldSetHealthMetricToOneIfAllConnectionsAreHealthy() { - when(sqsHealthProbe.isHealthy()).thenReturn(true); - when(unhandledSnsHealthProbe.isHealthy()).thenReturn(true); - when(suspensionsSnsHealthProbe.isHealthy()).thenReturn(true); - - HealthMetricPublisher healthPublisher = new HealthMetricPublisher(metricPublisher, probe); - healthPublisher.publishHealthStatus(); - verify(metricPublisher,times(1)).publishMetric("Health", 1.0); - } - -} diff --git a/services/nems-event-processor/src/test/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisherTest.java b/services/nems-event-processor/src/test/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisherTest.java deleted file mode 100644 index 1c875ac0..00000000 --- a/services/nems-event-processor/src/test/java/uk/nhs/prm/deductions/nemseventprocessor/metrics/MetricPublisherTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package uk.nhs.prm.deductions.nemseventprocessor.metrics; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; -import software.amazon.awssdk.services.cloudwatch.CloudWatchClient; -import software.amazon.awssdk.services.cloudwatch.model.Dimension; -import software.amazon.awssdk.services.cloudwatch.model.PutMetricDataRequest; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@ExtendWith(MockitoExtension.class) -class MetricPublisherTest { - - @Captor - private ArgumentCaptor putRequestCaptor; - - - @Test - public void shouldSetHealthMetricDimensionToAppropriateEnvironment() { - AppConfig config = Mockito.mock(AppConfig.class); - CloudWatchClient metricsClient = Mockito.mock(CloudWatchClient.class); - when(config.environment()).thenReturn("performance"); - - MetricPublisher metricPublisher = new MetricPublisher(metricsClient, config); - metricPublisher.publishMetric("Health", 0.0); - - verify(metricsClient).putMetricData(putRequestCaptor.capture()); - Dimension environmentDimension = putRequestCaptor.getValue().metricData().get(0).dimensions().get(0); - - assertThat(environmentDimension.name()).isEqualTo("Environment"); - assertThat(environmentDimension.value()).isEqualTo("performance"); - } -} diff --git a/services/pds-adaptor/build.gradle b/services/pds-adaptor/build.gradle index bb8109b6..1349649e 100644 --- a/services/pds-adaptor/build.gradle +++ b/services/pds-adaptor/build.gradle @@ -1,9 +1,8 @@ plugins { - id 'org.springframework.boot' version '2.7.18' - id 'io.spring.dependency-management' version '1.1.4' + id 'org.springframework.boot' version '4.0.3' + id 'io.spring.dependency-management' version '1.1.7' id 'java' id 'jacoco' - id 'com.github.spotbugs' version '6.0.6' } group = 'uk.nhs.prm.deductions' @@ -13,12 +12,19 @@ configurations { compileOnly { extendsFrom annotationProcessor } + mockitoAgent } repositories { mavenCentral() } +java { + toolchain { + languageVersion = JavaLanguageVersion.of(25) + } +} + //Without this task two jars are built, the additional "-plain.jar" is not needed // for more details refer to: https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#packaging-executable.and-plain-archives jar { @@ -26,41 +32,43 @@ jar { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-webmvc' implementation 'org.springframework.boot:spring-boot-starter-actuator' implementation 'org.springframework.boot:spring-boot-starter-security' + implementation 'org.springframework.boot:spring-boot-starter-validation' testImplementation 'org.springframework.boot:spring-boot-starter-test' - testImplementation 'org.springframework.boot:spring-boot-starter-aop' - - implementation 'org.yaml:snakeyaml:2.2' - implementation 'org.springdoc:springdoc-openapi-ui:1.6.15' - implementation 'net.logstash.logback:logstash-logback-encoder:6.6' - implementation 'org.bouncycastle:bcpkix-jdk15on:1.70' - implementation 'com.nimbusds:nimbus-jose-jwt:9.37.3' - implementation 'org.jetbrains:annotations:20.1.0' - implementation 'org.json:json:20231013' - implementation 'org.apache.httpcomponents:httpclient:4.5.14' - - implementation('io.netty:netty-buffer') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec-http') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec-http2') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-common') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-handler') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-resolver') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport-classes-epoll') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport-native-unix-common') { version { strictly '4.1.104.Final' } } - - implementation platform('software.amazon.awssdk:bom:2.18.41') + testImplementation 'org.springframework.boot:spring-boot-starter-webmvc-test' + testImplementation 'org.springframework.boot:spring-boot-starter-restclient-test' + + implementation 'org.yaml:snakeyaml:2.5' + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:3.0.2' + implementation 'net.logstash.logback:logstash-logback-encoder:9.0' + implementation 'org.bouncycastle:bcpkix-jdk18on:1.83' + implementation 'com.nimbusds:nimbus-jose-jwt:10.8' + implementation 'org.json:json:20251224' + + implementation('io.netty:netty-buffer') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec-http') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec-http2') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-common') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-handler') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-resolver') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport-classes-epoll') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport-native-unix-common') { version { strictly '4.2.9.Final' } } + + implementation platform('software.amazon.awssdk:bom:2.41.14') implementation 'software.amazon.awssdk:ssm' - compileOnly 'org.projectlombok:lombok:1.18.30' + compileOnly 'org.projectlombok:lombok:1.18.42' annotationProcessor 'org.projectlombok:lombok' - testImplementation 'com.github.tomakehurst:wiremock-jre8:2.35.1' + testImplementation 'org.wiremock:wiremock-standalone:3.13.2' + implementation 'org.apache.httpcomponents.client5:httpclient5' + mockitoAgent('org.mockito:mockito-core') { transitive = false } } springBoot { @@ -87,6 +95,7 @@ test { tasks.withType(Test) { useJUnitPlatform() + jvmArgs "-javaagent:${configurations.mockitoAgent.asPath}" } task integration(type: Test) { @@ -118,33 +127,5 @@ jacocoTestCoverageVerification { } } -spotbugsMain { - ignoreFailures = true - reports { - html { - enabled = true - destination = file("$buildDir/reports/spotbugs/main/spotbugs.html") - stylesheet = 'fancy-hist.xsl' - } - } -} - -spotbugsTest { - enabled = false -} - -spotbugsIntegration { - ignoreFailures = true - reports { - html { - enabled = true - destination = file("$buildDir/reports/spotbugs/integration/spotbugs.html") - stylesheet = 'fancy-hist.xsl' - } - } -} - -check.dependsOn integration - test.outputs.upToDateWhen {false} integration.outputs.upToDateWhen {false} diff --git a/services/pds-adaptor/gradle.properties b/services/pds-adaptor/gradle.properties new file mode 100644 index 00000000..e69de29b diff --git a/services/pds-adaptor/gradle/wrapper/gradle-wrapper.properties b/services/pds-adaptor/gradle/wrapper/gradle-wrapper.properties index d2880ba8..5dc98dbc 100644 --- a/services/pds-adaptor/gradle/wrapper/gradle-wrapper.properties +++ b/services/pds-adaptor/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/ApplicationHealthIntegrationTest.java b/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/ApplicationHealthIntegrationTest.java deleted file mode 100644 index d3de4948..00000000 --- a/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/ApplicationHealthIntegrationTest.java +++ /dev/null @@ -1,25 +0,0 @@ -package uk.nhs.prm.deductions.pdsadaptor; - -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; - -import static org.assertj.core.api.Assertions.assertThat; - -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) -class ApplicationHealthIntegrationTest { - - @LocalServerPort - private int port; - - @Autowired - private TestRestTemplate restTemplate; - - @Test - void applicationIsHealthy() { - assertThat(restTemplate.getForObject("http://localhost:" + port + "/actuator/health", - String.class)).isEqualTo("{\"status\":\"UP\"}"); - } -} diff --git a/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/AuthIntegrationTest.java b/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/AuthIntegrationTest.java index 916503c6..99fc01db 100644 --- a/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/AuthIntegrationTest.java +++ b/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/AuthIntegrationTest.java @@ -3,14 +3,20 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; -import org.springframework.http.*; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.assertj.core.api.Assertions.assertThat; +@AutoConfigureTestRestTemplate @ExtendWith(SpringExtension.class) @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class AuthIntegrationTest { diff --git a/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/PdsAdaptorIntegrationTest.java b/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/PdsAdaptorIntegrationTest.java index 12853996..44333556 100644 --- a/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/PdsAdaptorIntegrationTest.java +++ b/services/pds-adaptor/src/integration/java/uk/nhs/prm/deductions/pdsadaptor/PdsAdaptorIntegrationTest.java @@ -8,9 +8,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.resttestclient.TestRestTemplate; +import org.springframework.boot.resttestclient.autoconfigure.AutoConfigureTestRestTemplate; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.web.client.TestRestTemplate; -import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.*; import org.springframework.test.context.ContextConfiguration; import uk.nhs.prm.deductions.pdsadaptor.model.SuspendedPatientStatus; @@ -23,6 +24,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static uk.nhs.prm.deductions.pdsadaptor.testing.MapBuilder.json; +@AutoConfigureTestRestTemplate @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @ContextConfiguration(initializers = {WireMockInitializer.class}) public class PdsAdaptorIntegrationTest { @@ -244,7 +246,7 @@ public void shouldRetryWhen503ErrorsFromPdsFhir() { .withRequestBody(equalToJson(fhirPatchJsonToUpdateMofTo("A1235"))) .whenScenarioStateIs(STARTED) .willReturn(aResponse() - .withStatus(503) // request unsuccessful with status code 500 + .withStatus(503) // request unsuccessful with status code 503 .withHeader("Content-Type", "text/xml") .withBody("Some content")) .willSetStateTo("TRIED_ONCE")); diff --git a/services/pds-adaptor/src/integration/resources/logback-test.xml b/services/pds-adaptor/src/integration/resources/logback-test.xml new file mode 100644 index 00000000..5806741c --- /dev/null +++ b/services/pds-adaptor/src/integration/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/AuthenticatingHttpClient.java b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/AuthenticatingHttpClient.java index b230d0fc..57b1953f 100644 --- a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/AuthenticatingHttpClient.java +++ b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/AuthenticatingHttpClient.java @@ -2,7 +2,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.jetbrains.annotations.NotNull; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -52,12 +51,10 @@ private boolean isUnauthorized(HttpStatusCodeException e) { return e.getStatusCode() == HttpStatus.UNAUTHORIZED; } - @NotNull private HttpHeaders withRefreshedAuthHeader(HttpHeaders headers) { return withAuthHeader(headers, authService.getNewAccessToken()); } - @NotNull private HttpHeaders withCurrentAuthHeader(HttpHeaders headers) { return withAuthHeader(headers, authService.getAccessToken()); } diff --git a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/exceptions/BadRequestException.java b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/exceptions/BadRequestException.java index b75a31cb..8a0f07a8 100644 --- a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/exceptions/BadRequestException.java +++ b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/client/exceptions/BadRequestException.java @@ -9,7 +9,7 @@ @ResponseStatus(value = HttpStatus.BAD_REQUEST) public class BadRequestException extends RuntimeException { public BadRequestException(HttpStatusCodeException exception) { - super("Received " + exception.getRawStatusCode() + " error from PDS FHIR: error: " + exception.getMessage()); - log.info("Received " + exception.getRawStatusCode() + " error from PDS FHIR: error: " + exception.getMessage()); + super("Received " + exception.getStatusCode().value() + " error from PDS FHIR: error: " + exception.getMessage()); + log.info("Received " + exception.getStatusCode().value() + " error from PDS FHIR: error: " + exception.getMessage()); } } \ No newline at end of file diff --git a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/RestTemplateConfig.java b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/RestTemplateConfig.java index 28c37227..2479d830 100644 --- a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/RestTemplateConfig.java +++ b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/RestTemplateConfig.java @@ -1,7 +1,10 @@ package uk.nhs.prm.deductions.pdsadaptor.configuration; import lombok.RequiredArgsConstructor; -import org.apache.http.impl.client.HttpClients; +import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.apache.hc.client5.http.impl.classic.HttpClients; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -17,10 +20,19 @@ public RestTemplate apacheBasedRestTemplate() { } private HttpComponentsClientHttpRequestFactory apacheHttpClientRequestFactory() { - HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); - requestFactory.setConnectTimeout(11000); + PoolingHttpClientConnectionManager connectionManager = + PoolingHttpClientConnectionManagerBuilder.create() + .setMaxConnTotal(50) + .setMaxConnPerRoute(10) + .build(); + + CloseableHttpClient httpClient = HttpClients.custom() + .setConnectionManager(connectionManager) + .build(); + + HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + requestFactory.setConnectionRequestTimeout(11000); requestFactory.setReadTimeout(11000); - requestFactory.setHttpClient(HttpClients.custom().setMaxConnTotal(50).setMaxConnPerRoute(10).build()); return requestFactory; } } diff --git a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/SecurityConfig.java b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/SecurityConfig.java index a2c8477c..ea4445b3 100644 --- a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/SecurityConfig.java +++ b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/configuration/SecurityConfig.java @@ -1,18 +1,21 @@ package uk.nhs.prm.deductions.pdsadaptor.configuration; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.Customizer; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.MessageDigestPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.provisioning.InMemoryUserDetailsManager; +import org.springframework.security.web.SecurityFilterChain; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.ssm.SsmClient; import uk.nhs.prm.deductions.pdsadaptor.service.ReadSSMParameter; @@ -22,50 +25,53 @@ @Configuration @EnableWebSecurity @Slf4j -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) -public class SecurityConfig extends WebSecurityConfigurerAdapter { +@EnableMethodSecurity(prePostEnabled = true, securedEnabled = true) +public class SecurityConfig { @Value("${environment}") private String environment; - public PasswordEncoder passwordEncoder = new MessageDigestPasswordEncoder("SHA-256"); + @Bean + public PasswordEncoder passwordEncoder() { + return new MessageDigestPasswordEncoder("SHA-256"); + } + + @Bean + public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { + log.info("setting up client auth"); + InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); - @Autowired - public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { - log.info("setting up client auth in configureGlobal"); if (!environment.equals("local") && !environment.equals("int-test")) { ReadSSMParameter ssmService = new ReadSSMParameter(createSsmClient()); Map userMap = ssmService.getApiKeys(environment); userMap.forEach((parameterName, apiKey) -> { - try { - String username = getUsernameFromParameter(parameterName); - auth.inMemoryAuthentication() - .passwordEncoder(passwordEncoder) - .withUser(username) + String username = getUsernameFromParameter(parameterName); + manager.createUser( + User.withUsername(username) .password(passwordEncoder.encode(apiKey)) - .roles("USER"); - } catch (Exception e) { - e.printStackTrace(); - } + .roles("USER") + .build() + ); }); } else { - //this for local usage only - auth.inMemoryAuthentication() - .passwordEncoder(passwordEncoder) - .withUser("admin") - .password(passwordEncoder.encode("admin")) - .roles("USER"); + // local usage only + manager.createUser( + User.withUsername("admin") + .password(passwordEncoder.encode("admin")) + .roles("USER") + .build() + ); } - log.info("completed configureGlobal"); + + log.info("completed auth setup"); + return manager; } private SsmClient createSsmClient() { - Region region = Region.EU_WEST_2; - SsmClient ssmClient = SsmClient.builder() - .region(region) - .build(); - return ssmClient; + return SsmClient.builder() + .region(Region.EU_WEST_2) + .build(); } private String getUsernameFromParameter(String parameterName) { @@ -74,29 +80,29 @@ private String getUsernameFromParameter(String parameterName) { } private static final String[] AUTH_WHITELIST = { - // -- Swagger UI v2 - "/v2/api-docs", - "/swagger-resources", - "/swagger-resources/**", - "/configuration/ui", - "/configuration/security", - "/swagger-ui.html", - "/swagger/**", - "/webjars/**", - "/v3/api-docs/**", - "/swagger-ui/**", - "/actuator/health" - // other public endpoints of your API may be appended to this array + "/v2/api-docs", + "/swagger-resources", + "/swagger-resources/**", + "/configuration/ui", + "/configuration/security", + "/swagger-ui.html", + "/swagger/**", + "/webjars/**", + "/v3/api-docs/**", + "/swagger-ui/**", + "/actuator/health" }; - @Override - protected void configure(HttpSecurity http) throws Exception { - http.csrf().disable() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(). - httpBasic().and(). - authorizeRequests(). - antMatchers(AUTH_WHITELIST).permitAll(). - antMatchers("/**").authenticated(); // require authentication for any endpoint that's not whitelisted - } + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .csrf(AbstractHttpConfigurer::disable) + .sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .httpBasic(Customizer.withDefaults()) + .authorizeHttpRequests(auth -> auth + .requestMatchers(AUTH_WHITELIST).permitAll() + .anyRequest().authenticated()); + return http.build(); + } } diff --git a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsController.java b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsController.java index 53110b37..15b7803d 100644 --- a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsController.java +++ b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsController.java @@ -11,8 +11,9 @@ import uk.nhs.prm.deductions.pdsadaptor.model.UpdateManagingOrganisationRequest; import uk.nhs.prm.deductions.pdsadaptor.service.PdsService; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + import java.security.Principal; @RestController @@ -27,8 +28,11 @@ public class PdsController { @Operation(security = @SecurityRequirement(name = "basicAuth")) @GetMapping("/{nhsNumber}") @ResponseStatus(HttpStatus.OK) - public SuspendedPatientStatus getPatientGpStatus(@PathVariable("nhsNumber") @NotBlank @Size(max = 10, min = 10) String nhsNumber, - @RequestHeader(value = "traceId", required = false) String traceId, Principal principal) { + public SuspendedPatientStatus getPatientGpStatus( + @PathVariable("nhsNumber") @NotBlank @Size(max = 10, min = 10) String nhsNumber, + @RequestHeader(value = "traceId", required = false) String traceId, + Principal principal + ) { tracer.setTraceId(traceId); log.info("Request for pds record received from {}", principal.getName()); return pdsService.getPatientGpStatus(nhsNumber); @@ -37,9 +41,12 @@ public SuspendedPatientStatus getPatientGpStatus(@PathVariable("nhsNumber") @Not @Operation(security = @SecurityRequirement(name = "basicAuth")) @PutMapping("/{nhsNumber}") @ResponseStatus(HttpStatus.OK) - public SuspendedPatientStatus updatePatientManagingOrganisation(@PathVariable("nhsNumber") @NotBlank @Size(max = 10, min = 10) String nhsNumber, - @RequestBody UpdateManagingOrganisationRequest updateRequest, - @RequestHeader(value = "traceId", required = false) String traceId, Principal principal) { + public SuspendedPatientStatus updatePatientManagingOrganisation( + @PathVariable("nhsNumber") @NotBlank @Size(max = 10, min = 10) String nhsNumber, + @RequestBody UpdateManagingOrganisationRequest updateRequest, + @RequestHeader(value = "traceId", required = false) String traceId, + Principal principal + ) { tracer.setTraceId(traceId); log.info("Update request for pds record received from {}", principal.getName()); return pdsService.updatePatientManagingOrganisation(nhsNumber, updateRequest); diff --git a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/model/UpdateManagingOrganisationRequest.java b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/model/UpdateManagingOrganisationRequest.java index 3caee0ec..2641bbc0 100644 --- a/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/model/UpdateManagingOrganisationRequest.java +++ b/services/pds-adaptor/src/main/java/uk/nhs/prm/deductions/pdsadaptor/model/UpdateManagingOrganisationRequest.java @@ -6,8 +6,8 @@ import lombok.Data; import lombok.NoArgsConstructor; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; @Builder @Data diff --git a/services/pds-adaptor/src/main/resources/logback.xml b/services/pds-adaptor/src/main/resources/logback.xml index c914597b..e3d43b8d 100644 --- a/services/pds-adaptor/src/main/resources/logback.xml +++ b/services/pds-adaptor/src/main/resources/logback.xml @@ -1,6 +1,5 @@ - @@ -73,6 +72,7 @@ + diff --git a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/PdsFhirExceptionHandlerTest.java b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/PdsFhirExceptionHandlerTest.java index c7bf6015..c1e75bd0 100644 --- a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/PdsFhirExceptionHandlerTest.java +++ b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/PdsFhirExceptionHandlerTest.java @@ -1,7 +1,7 @@ package uk.nhs.prm.deductions.pdsadaptor.client; +import ch.qos.logback.classic.spi.ILoggingEvent; import net.logstash.logback.marker.RawJsonAppendingMarker; -import org.jetbrains.annotations.NotNull; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -13,8 +13,10 @@ import org.springframework.web.client.UnknownContentTypeException; import uk.nhs.prm.deductions.pdsadaptor.client.exceptions.*; import uk.nhs.prm.deductions.pdsadaptor.model.pdsresponse.PdsFhirPatient; +import uk.nhs.prm.deductions.pdsadaptor.testing.TestLogAppender; import java.util.HashMap; +import java.util.Map; import static java.nio.charset.StandardCharsets.UTF_8; import static org.assertj.core.api.Assertions.assertThat; @@ -33,34 +35,37 @@ void setUp() { } @Test - public void shouldStructurallyLogTheResponseBodyForAHttp404() { - var testLogAppender = addTestLogAppender(); + public void shouldStructurallyLogTheResponseBodyOnNotFound() { + TestLogAppender testLogAppender = addTestLogAppender(); - var responseBody = new HashMap<>() { + Map responseBody = new HashMap<>() { { - put("some_code", "yo code"); - put("some_detail", "hey detail"); + put("some_code", "example code"); + put("some_detail", "example detail"); } }; assertThrows(RuntimeException.class, () -> handler.handleCommonExceptions("some description", createErrorResponse(404, asJson(responseBody)))); - var logged = testLogAppender.getLastLoggedEvent(); + ILoggingEvent logged = testLogAppender.getLastLoggedEvent(); assertThat(logged.getMarker()).isInstanceOf(RawJsonAppendingMarker.class); - var jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); + RawJsonAppendingMarker jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); assertThat(jsonMarker.getFieldName()).isEqualTo("error_response"); + assertThat(logged.getFormattedMessage()).isEqualTo("PDS FHIR Request failed - Patient not found 404"); - var loggedJson = (String) jsonMarker.getFieldValue(); - assertThat(loggedJson.contains("some_code")).isTrue(); - assertThat(loggedJson.contains("some_detail")).isTrue(); + String markerOutput = jsonMarker.toString(); + assertThat(markerOutput).contains("some_code"); + assertThat(markerOutput).contains("example code"); + assertThat(markerOutput).contains("some_detail"); + assertThat(markerOutput).contains("example detail"); } @Test void shouldThrowBadRequestExceptionWhenPdsResourceInvalid() { - var badRequest400 = new HttpClientErrorException(BAD_REQUEST, "bad-request-error"); + HttpClientErrorException badRequest400 = new HttpClientErrorException(BAD_REQUEST, "bad-request-error"); - var exception = assertThrows(BadRequestException.class, () -> + BadRequestException exception = assertThrows(BadRequestException.class, () -> handler.handleCommonExceptions("context", badRequest400)); assertThat(exception.getMessage()) @@ -69,19 +74,19 @@ void shouldThrowBadRequestExceptionWhenPdsResourceInvalid() { @Test void shouldThrowNotFoundExceptionIfPatientNotFoundInPds() { - var notFound404 = new HttpClientErrorException(HttpStatus.NOT_FOUND, "error"); + HttpClientErrorException notFound404 = new HttpClientErrorException(HttpStatus.NOT_FOUND, "error"); - var exception = assertThrows(NotFoundException.class, () -> + NotFoundException exception = assertThrows(NotFoundException.class, () -> handler.handleCommonExceptions("context", notFound404)); assertThat(exception.getMessage()).isEqualTo("PDS FHIR Request failed - Patient not found 404"); } @Test - void shouldThrowAccessTokenRequestExceptionOnForbiddenError___reallyThisIsSuchAComplicatedInteractionAndTotallyUnexpectedSoIfIGetForbiddenFromPdsFhirClientImMeantToAssumeItWasDownToTheAccessTokenRequest__itDoesntSeemReasonable___surelyIfItsSomethingToDoWithReauthenticationItsNothingToDoWithPdsFhirClientPerSeItsAConcernOftheAuthenticatingHttpClient() { - var forbiddenError403 = new HttpClientErrorException(HttpStatus.FORBIDDEN, "error"); + void shouldThrowAccessTokenRequestExceptionOnForbiddenError() { + HttpClientErrorException forbiddenError403 = new HttpClientErrorException(HttpStatus.FORBIDDEN, "error"); - var exception = assertThrows(AccessTokenRequestException.class, () -> + AccessTokenRequestException exception = assertThrows(AccessTokenRequestException.class, () -> handler.handleCommonExceptions("context", forbiddenError403)); assertThat(exception.getMessage()).contains("Access token request failed"); @@ -90,9 +95,9 @@ void shouldThrowAccessTokenRequestExceptionOnForbiddenError___reallyThisIsSuchAC @Test void shouldThrowAccessTokenRequestExceptionOnUnauthorizedError() { - var unauthorizedError401 = new HttpClientErrorException(HttpStatus.UNAUTHORIZED, "error"); + HttpClientErrorException unauthorizedError401 = new HttpClientErrorException(HttpStatus.UNAUTHORIZED, "error"); - var exception = assertThrows(AccessTokenRequestException.class, () -> + AccessTokenRequestException exception = assertThrows(AccessTokenRequestException.class, () -> handler.handleCommonExceptions("context", unauthorizedError401)); assertThat(exception.getMessage()).contains("Access token request failed"); @@ -101,29 +106,29 @@ void shouldThrowAccessTokenRequestExceptionOnUnauthorizedError() { @Test void shouldThrowTooManyRequestsExceptionWhenExceedingPdsFhirRateLimit() { - var tooManyRequests429 = new HttpClientErrorException(HttpStatus.TOO_MANY_REQUESTS, "error"); + HttpClientErrorException tooManyRequests429 = new HttpClientErrorException(HttpStatus.TOO_MANY_REQUESTS, "error"); - var exception = assertThrows(TooManyRequestsException.class, () -> + TooManyRequestsException exception = assertThrows(TooManyRequestsException.class, () -> handler.handleCommonExceptions("context", tooManyRequests429)); assertThat(exception.getMessage()).isEqualTo("Rate limit exceeded for PDS FHIR - too many requests"); } @Test - void whenPdsFhirIsApparentlyUnavailableShouldThrowExceptionDenotingThatItIsPossiblyATemporaryIssueAndThereforeProbablyUsefulToRetry() { - var pdsServiceIsUnavailable503 = new HttpServerErrorException(HttpStatus.SERVICE_UNAVAILABLE, "error"); + void shouldThrowRetryableRequestExceptionOnServiceUnavailable() { + HttpServerErrorException pdsServiceIsUnavailable503 = new HttpServerErrorException(HttpStatus.SERVICE_UNAVAILABLE, "error"); - var exception = assertThrows(RetryableRequestException.class, () -> + RetryableRequestException exception = assertThrows(RetryableRequestException.class, () -> handler.handleCommonExceptions("context", pdsServiceIsUnavailable503)); assertThat(exception.getMessage()).isEqualTo("PDS FHIR request failed status code: 503. reason 503 error"); } @Test - void whenThereIsANetworkFailureOrTimeoutThrowExceptionThatItIsProbablyUsefulToRetry() { - var networkFailure = new ResourceAccessException("something like a socket timeout"); + void shouldThrowRetryableRequestExceptionOnNetworkFailure() { + ResourceAccessException networkFailure = new ResourceAccessException("something like a socket timeout"); - var exception = assertThrows(RetryableRequestException.class, () -> + RetryableRequestException exception = assertThrows(RetryableRequestException.class, () -> handler.handleCommonExceptions("context", networkFailure)); assertThat(exception.getCause()).isEqualTo(networkFailure); @@ -131,11 +136,11 @@ void whenThereIsANetworkFailureOrTimeoutThrowExceptionThatItIsProbablyUsefulToRe } @Test - void shouldThrowRuntimeExceptionWhenClientCannotParseSeeminglySuccessfulResponse____feelsLikeImplementationDetailLowerDownShouldMoveIntoHttpClient() { - var unparseableResponseException = new UnknownContentTypeException(PdsFhirPatient.class, APPLICATION_JSON, 200, + void shouldThrowRuntimeExceptionWhenResponseCannotBeParsed() { + UnknownContentTypeException unparseableResponseException = new UnknownContentTypeException(PdsFhirPatient.class, APPLICATION_JSON, 200, "ok", new HttpHeaders(), new byte[0]); - var exception = assertThrows(RuntimeException.class, () -> + RuntimeException exception = assertThrows(RuntimeException.class, () -> handler.handleCommonExceptions("requesting", unparseableResponseException)); assertThat(exception.getMessage()).contains("PDS FHIR returned unexpected response body when requesting PDS Record"); @@ -143,9 +148,9 @@ void shouldThrowRuntimeExceptionWhenClientCannotParseSeeminglySuccessfulResponse @Test void shouldRethrowInitialAccessTokenRequestException() { - var exceptionFromAuthenticationStack = new AccessTokenRequestException(new HttpServerErrorException(HttpStatus.SERVICE_UNAVAILABLE)); + AccessTokenRequestException exceptionFromAuthenticationStack = new AccessTokenRequestException(new HttpServerErrorException(HttpStatus.SERVICE_UNAVAILABLE)); - var exception = assertThrows(RuntimeException.class, () -> + RuntimeException exception = assertThrows(RuntimeException.class, () -> handler.handleCommonExceptions("requesting", exceptionFromAuthenticationStack)); assertThat(exception).isEqualTo(exceptionFromAuthenticationStack); @@ -153,21 +158,19 @@ void shouldRethrowInitialAccessTokenRequestException() { @Test void shouldRethrowInitialRuntimeExceptionWhenNotSpecificallyHandled() { - var unexpectedException = new IllegalArgumentException("not anticipated"); + IllegalArgumentException unexpectedException = new IllegalArgumentException("not anticipated"); - var exception = assertThrows(RuntimeException.class, () -> + RuntimeException exception = assertThrows(RuntimeException.class, () -> handler.handleCommonExceptions("requesting", unexpectedException)); assertThat(exception).isEqualTo(unexpectedException); } - @NotNull private HttpClientErrorException createErrorResponse(int statusCode, String responseBodyJson) { return new HttpClientErrorException(HttpStatus.resolve(statusCode), "error", responseBodyJson.getBytes(UTF_8), UTF_8); } - private String asJson(HashMap errorResponseBodyContent) { + private String asJson(Map errorResponseBodyContent) { return new JSONObject(errorResponseBodyContent).toString(); } - -} \ No newline at end of file +} diff --git a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/RetryingPdsFhirClientTest.java b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/RetryingPdsFhirClientTest.java index 5347217e..efc29eed 100644 --- a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/RetryingPdsFhirClientTest.java +++ b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/client/RetryingPdsFhirClientTest.java @@ -1,6 +1,5 @@ package uk.nhs.prm.deductions.pdsadaptor.client; -import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -97,12 +96,10 @@ void shouldRetryUpdateIfCannotGetThroughToPdsAndReturnSuccessfulResponseIfThenSu assertThat(response).isEqualTo(successfulPdsResponse); } - @NotNull private HttpServerErrorException aServerException() { return new HttpServerErrorException(SERVICE_UNAVAILABLE, "error"); } - @NotNull private UpdateManagingOrganisationRequest anUpdateRequest() { return new UpdateManagingOrganisationRequest("ODS123", "someTag"); } diff --git a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsControllerTest.java b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsControllerTest.java index 6d2c9ade..00b49fe1 100644 --- a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsControllerTest.java +++ b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/controller/PdsControllerTest.java @@ -3,14 +3,11 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; -import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest; import org.springframework.http.MediaType; +import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import uk.nhs.prm.deductions.pdsadaptor.client.exceptions.AccessTokenRequestException; import uk.nhs.prm.deductions.pdsadaptor.client.exceptions.BadRequestException; @@ -33,7 +30,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static uk.nhs.prm.deductions.pdsadaptor.testing.MapBuilder.json; -@ExtendWith(MockitoExtension.class) @WebMvcTest(PdsController.class) @AutoConfigureMockMvc(addFilters = false) class PdsControllerTest { @@ -43,10 +39,10 @@ class PdsControllerTest { @Autowired private MockMvc mockMvc; - @MockBean + @MockitoBean private PdsService pdsService; - @MockBean + @MockitoBean private Tracer tracer; @Test @@ -88,10 +84,10 @@ void shouldCallPdsFhirApiWithNhsNumberAndUpdateRequest() throws Exception { when(pdsService.updatePatientManagingOrganisation(NHS_NUMBER, updateRequest)).thenReturn(actualSuspendedPatientStatus); doCallRealMethod().when(tracer).setTraceId("fake-trace-id"); - var principal = mock(Principal.class); + Principal principal = mock(Principal.class); when(principal.getName()).thenReturn("fake-user"); - var contentAsString = mockMvc.perform(put("/suspended-patient-status/" + NHS_NUMBER) + String contentAsString = mockMvc.perform(put("/suspended-patient-status/" + NHS_NUMBER) .header("traceId", "fake-trace-id") .principal(principal) .contentType(MediaType.APPLICATION_JSON) @@ -104,7 +100,7 @@ void shouldCallPdsFhirApiWithNhsNumberAndUpdateRequest() throws Exception { .andReturn().getResponse().getContentAsString(); verify(pdsService).updatePatientManagingOrganisation(NHS_NUMBER, updateRequest); - var suspendedPatientStatus = objectMapper.readValue(contentAsString, SuspendedPatientStatus.class); + SuspendedPatientStatus suspendedPatientStatus = objectMapper.readValue(contentAsString, SuspendedPatientStatus.class); assertThat(suspendedPatientStatus).isEqualTo(actualSuspendedPatientStatus); diff --git a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/logging/JsonLoggerTest.java b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/logging/JsonLoggerTest.java index 063273a8..1051be38 100644 --- a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/logging/JsonLoggerTest.java +++ b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/logging/JsonLoggerTest.java @@ -1,12 +1,15 @@ package uk.nhs.prm.deductions.pdsadaptor.logging; import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; import net.logstash.logback.marker.RawJsonAppendingMarker; import org.json.JSONObject; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; +import uk.nhs.prm.deductions.pdsadaptor.testing.TestLogAppender; +import java.lang.reflect.Field; import java.util.HashMap; import static org.assertj.core.api.Assertions.assertThat; @@ -24,57 +27,57 @@ public void createLogger() { @Test public void shouldStructurallyLogMessageAsInfoWithNamedJsonField() { - var testLogAppender = addTestLogAppender(); + TestLogAppender testLogAppender = addTestLogAppender(); - var json = asJson(new HashMap<>() { + String json = asJson(new HashMap<>() { { - put("some_code", "yo code"); - put("some_detail", "hey detail"); + put("some_code", "example code"); + put("some_detail", "example detail"); } }); JsonLogger.logInfoWithJson(log, "the message", "json_fieldname", json); - var logged = testLogAppender.getLastLoggedEvent(); + ILoggingEvent logged = testLogAppender.getLastLoggedEvent(); assertThat(logged.getMessage()).isEqualTo("the message"); assertThat(logged.getLevel()).isEqualTo(Level.INFO); assertThat(logged.getMarker()).isInstanceOf(RawJsonAppendingMarker.class); - var jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); + RawJsonAppendingMarker jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); assertThat(jsonMarker.getFieldName()).isEqualTo("json_fieldname"); - var loggedJson = (String) jsonMarker.getFieldValue(); + String loggedJson = getMarkerFieldValue(jsonMarker); assertThat(loggedJson.contains("some_code")).isTrue(); assertThat(loggedJson.contains("some_detail")).isTrue(); } @Test public void shouldRemoveLineBreaksWithinJsonWhenStructurallyLoggingWithJson() { - var testLogAppender = addTestLogAppender(); + TestLogAppender testLogAppender = addTestLogAppender(); - var jsonWithLineBreak = "{\n" + + String jsonWithLineBreak = "{\n" + "\"a_field\": \"some value\"}"; JsonLogger.logInfoWithJson(log, "msg", "json_name", jsonWithLineBreak); - var logged = testLogAppender.getLastLoggedEvent(); - var jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); - var loggedJson = (String) jsonMarker.getFieldValue(); + ILoggingEvent logged = testLogAppender.getLastLoggedEvent(); + RawJsonAppendingMarker jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); + String loggedJson = getMarkerFieldValue(jsonMarker); assertThat(loggedJson.contains("{\"a_field")).isTrue(); } @Test public void shouldLogRawResponseWhenNotValidJsonAndLoggingWithJson() { - var testLogAppender = addTestLogAppender(); + TestLogAppender testLogAppender = addTestLogAppender(); JsonLogger.logInfoWithJson(log, "some message", "it's invalid you know", "invalid-json"); - var logged = testLogAppender.getLastLoggedEvent(); + ILoggingEvent logged = testLogAppender.getLastLoggedEvent(); - var jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); + RawJsonAppendingMarker jsonMarker = (RawJsonAppendingMarker) logged.getMarker(); assertThat(jsonMarker.getFieldName()).isEqualTo("it's invalid you know"); - var loggedJson = (String) jsonMarker.getFieldValue(); + String loggedJson = getMarkerFieldValue(jsonMarker); assertThat(loggedJson).isEqualTo("invalid-json"); } @@ -82,4 +85,14 @@ private String asJson(HashMap errorResponseBodyContent) { return new JSONObject(errorResponseBodyContent).toString(); } + private String getMarkerFieldValue(RawJsonAppendingMarker marker) { + try { + Field field = RawJsonAppendingMarker.class.getDeclaredField("rawJson"); + field.setAccessible(true); + return (String) field.get(marker); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException("Could not access rawJson on RawJsonAppendingMarker", e); + } + } + } \ No newline at end of file diff --git a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/service/ReadSSMParameterTest.java b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/service/ReadSSMParameterTest.java index e3c28f0c..88ca12ca 100644 --- a/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/service/ReadSSMParameterTest.java +++ b/services/pds-adaptor/src/test/java/uk/nhs/prm/deductions/pdsadaptor/service/ReadSSMParameterTest.java @@ -15,8 +15,9 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -31,7 +32,6 @@ public class ReadSSMParameterTest { private Parameter user2; private Parameter service1; - @BeforeEach void setup() { ssmClient = Mockito.mock(SsmClient.class); @@ -39,12 +39,12 @@ void setup() { getParametersByPathRequestForService = GetParametersByPathRequest.builder() .withDecryption(Boolean.TRUE) - .path("/repo/" + "local" + "/user-input/api-keys/pds-adaptor/") + .path("/repo/local/user-input/api-keys/pds-adaptor/") .build(); getParametersByPathRequestForUser = GetParametersByPathRequest.builder() .withDecryption(Boolean.TRUE) - .path("/repo/" + "local" + "/user-input/api-keys/pds-adaptor/api-key-user") + .path("/repo/local/user-input/api-keys/pds-adaptor/api-key-user") .build(); user1 = Parameter.builder().name("user1").type(ParameterType.SECURE_STRING).value("12345").build(); @@ -53,15 +53,21 @@ void setup() { } @Test - void shouldCallGetParameterByPathAndReturnMapOfApiKeys(){ + void shouldCallGetParameterByPathAndReturnMapOfApiKeys() { // setup - GetParametersByPathResponse userApiKeyParameters = GetParametersByPathResponse.builder().parameters(Arrays.asList(user1, user2)).build(); - GetParametersByPathResponse serviceApiKeyParameters = GetParametersByPathResponse.builder().parameters(List.of(service1)).build(); - GetParametersByPathIterable iterableService = new GetParametersByPathIterable(ssmClient, getParametersByPathRequestForService); - GetParametersByPathIterable iterableUser = new GetParametersByPathIterable(ssmClient, getParametersByPathRequestForUser); + GetParametersByPathResponse serviceApiKeyParameters = GetParametersByPathResponse.builder() + .parameters(List.of(service1)) + .build(); + GetParametersByPathResponse userApiKeyParameters = GetParametersByPathResponse.builder() + .parameters(Arrays.asList(user1, user2)) + .build(); + + GetParametersByPathIterable iterableService = Mockito.mock(GetParametersByPathIterable.class); + GetParametersByPathIterable iterableUser = Mockito.mock(GetParametersByPathIterable.class); + + when(iterableService.stream()).thenReturn(Stream.of(serviceApiKeyParameters)); + when(iterableUser.stream()).thenReturn(Stream.of(userApiKeyParameters)); - when(ssmClient.getParametersByPath(getParametersByPathRequestForService)).thenReturn(userApiKeyParameters); - when(ssmClient.getParametersByPath(getParametersByPathRequestForUser)).thenReturn(serviceApiKeyParameters); when(ssmClient.getParametersByPathPaginator(getParametersByPathRequestForService)).thenReturn(iterableService); when(ssmClient.getParametersByPathPaginator(getParametersByPathRequestForUser)).thenReturn(iterableUser); @@ -69,12 +75,12 @@ void shouldCallGetParameterByPathAndReturnMapOfApiKeys(){ Map apiKeys = ssmParamService.getApiKeys("local"); // assertions - verify(ssmClient).getParametersByPath(getParametersByPathRequestForService); - verify(ssmClient).getParametersByPath(getParametersByPathRequestForUser); + verify(ssmClient).getParametersByPathPaginator(getParametersByPathRequestForService); + verify(ssmClient).getParametersByPathPaginator(getParametersByPathRequestForUser); - assertTrue(apiKeys.containsKey("user1") && apiKeys.containsValue("12345")); - assertTrue(apiKeys.containsKey("user2") && apiKeys.containsValue("56789")); - assertTrue(apiKeys.containsKey("service1") && apiKeys.containsValue("54321")); + assertEquals("12345", apiKeys.get("user1")); + assertEquals("56789", apiKeys.get("user2")); + assertEquals("54321", apiKeys.get("service1")); + assertEquals(3, apiKeys.size()); } - } diff --git a/services/pds-adaptor/tasks b/services/pds-adaptor/tasks index 82ebb2b2..2532a516 100755 --- a/services/pds-adaptor/tasks +++ b/services/pds-adaptor/tasks @@ -40,10 +40,6 @@ case "${command}" in configure_local_envs ./gradlew test ;; - test_unit_cached) - configure_local_envs - ./tasks test_unit - ;; test_integration) configure_local_envs ./gradlew --info integration @@ -53,10 +49,6 @@ case "${command}" in _assume_environment_role $NHS_ENVIRONMENT test_performance ;; - test_coverage) - configure_local_envs - ./gradlew jacocoTestCoverageVerification - ;; code_quality) configure_local_envs ./gradlew check -x test -x integration @@ -73,26 +65,6 @@ case "${command}" in configure_local_run_against_aws_env ./gradlew bootRun ;; - tf) - check_env - tf_init - bash - ;; - tf_plan) - check_env - _assume_environment_role $NHS_ENVIRONMENT - tf_plan "$2" - ;; - tf_apply) - check_env - _assume_environment_role $NHS_ENVIRONMENT - tf_apply - ;; - promote_docker_image) - check_env - set_image_tag - promote_docker_image "$IMAGE_REPO_NAME:$IMAGE_TAG" "$NHS_ENVIRONMENT" - ;; wait_ecs) check_env _assume_environment_role $NHS_ENVIRONMENT diff --git a/services/re-registration-service/Dockerfile b/services/re-registration-service/Dockerfile index ded9919c..748d52e9 100644 --- a/services/re-registration-service/Dockerfile +++ b/services/re-registration-service/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:21-jre-alpine-3.23 +FROM eclipse-temurin:25-jre-alpine-3.23 RUN apk update && \ apk -u list && \ diff --git a/services/re-registration-service/build.gradle b/services/re-registration-service/build.gradle index 64f6e264..1455fafc 100644 --- a/services/re-registration-service/build.gradle +++ b/services/re-registration-service/build.gradle @@ -1,15 +1,13 @@ plugins { - id 'org.springframework.boot' version '3.2.1' - id 'io.spring.dependency-management' version '1.1.4' + id 'org.springframework.boot' version '4.0.3' + id 'io.spring.dependency-management' version '1.1.7' id 'java' id 'jacoco' - id 'com.github.spotbugs' version '6.0.6' id "org.sonarqube" version "4.4.1.3373" } group = 'uk.nhs.prm.repo' version = '0.0.1-SNAPSHOT' -sourceCompatibility = '21' configurations { compileOnly { @@ -21,6 +19,12 @@ repositories { mavenCentral() } +java { + toolchain { + languageVersion = JavaLanguageVersion.of(25) + } +} + //Without this task two jars are built, the additional "-plain.jar" is not needed // for more details refer to: https://docs.spring.io/spring-boot/docs/current/gradle-plugin/reference/htmlsingle/#packaging-executable.and-plain-archives jar { @@ -28,50 +32,45 @@ jar { } dependencies { - implementation('com.google.guava:guava') { version { strictly '32.1.3-jre' } } - - implementation('org.yaml:snakeyaml') { version { strictly '2.2' } } - implementation 'org.springframework.boot:spring-boot-starter' implementation 'org.springframework.boot:spring-boot-starter-web' - implementation('io.netty:netty-buffer') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec-http') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-codec-http2') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-common') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-handler') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-resolver') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport-classes-epoll') { version { strictly '4.1.104.Final' } } - implementation('io.netty:netty-transport-native-unix-common') { version { strictly '4.1.104.Final' } } + implementation 'org.yaml:snakeyaml:2.5' + + implementation('io.netty:netty-buffer') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec-http') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-codec-http2') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-common') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-handler') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-resolver') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport-classes-epoll') { version { strictly '4.2.9.Final' } } + implementation('io.netty:netty-transport-native-unix-common') { version { strictly '4.2.9.Final' } } - implementation 'com.amazonaws:amazon-sqs-java-messaging-lib:1.1.2' + implementation 'com.fasterxml.jackson.core:jackson-databind' - implementation platform('software.amazon.awssdk:bom:2.18.41') + implementation 'com.amazonaws:amazon-sqs-java-messaging-lib:2.1.4' + + implementation platform('software.amazon.awssdk:bom:2.41.14') implementation 'software.amazon.awssdk:cloudwatch' implementation 'software.amazon.awssdk:sqs' implementation 'software.amazon.awssdk:sns' implementation 'software.amazon.awssdk:dynamodb' - implementation group: 'javax.inject', name: 'javax.inject', version: '1' - implementation 'org.springframework:spring-jms:5.3.19' - implementation 'com.google.code.gson:gson:2.10.1' - implementation 'io.github.resilience4j:resilience4j-retry:1.7.1' + implementation 'com.google.code.gson:gson:2.13.2' + implementation 'io.github.resilience4j:resilience4j-retry:2.3.0' - implementation 'net.logstash.logback:logstash-logback-encoder:7.2' + implementation 'net.logstash.logback:logstash-logback-encoder:9.0' - compileOnly 'org.projectlombok:lombok:1.18.24' + compileOnly 'org.projectlombok:lombok:1.18.42' annotationProcessor 'org.projectlombok:lombok' - implementation 'com.github.spotbugs:spotbugs-annotations:4.8.3' - testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-test' - testImplementation "org.mockito:mockito-core:3.+" - testImplementation 'org.wiremock:wiremock-standalone:3.3.1' - testImplementation 'org.awaitility:awaitility:4.2.0' + testImplementation 'org.wiremock:wiremock-standalone:3.13.2' + testImplementation 'org.awaitility:awaitility:4.3.0' } test { @@ -129,43 +128,6 @@ jacocoTestCoverageVerification { } } -spotbugs { - toolVersion = '4.8.3' -} - -spotbugsMain { - ignoreFailures = true - reports { - html { - enabled = true - destination = file("$buildDir/reports/spotbugs/main/spotbugs.html") - stylesheet = 'fancy-hist.xsl' - } - } -} - -spotbugsTest { - ignoreFailures = true - reports { - html { - enabled = true - destination = file("$buildDir/reports/spotbugs/test/spotbugs.html") - stylesheet = 'fancy-hist.xsl' - } - } -} - -spotbugsIntegration { - ignoreFailures = true - reports { - html { - enabled = true - destination = file("$buildDir/reports/spotbugs/integration/spotbugs.html") - stylesheet = 'fancy-hist.xsl' - } - } -} - sonar { properties { property "sonar.projectKey", "prm-orphaned-record-continuity_prm-repo-re-registration-service" diff --git a/services/re-registration-service/gradle/wrapper/gradle-wrapper.properties b/services/re-registration-service/gradle/wrapper/gradle-wrapper.properties index a5952066..5dc98dbc 100644 --- a/services/re-registration-service/gradle/wrapper/gradle-wrapper.properties +++ b/services/re-registration-service/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ActiveSuspensionsIntegrationTest.java b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ActiveSuspensionsIntegrationTest.java index 3576e4f3..4008bf77 100644 --- a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ActiveSuspensionsIntegrationTest.java +++ b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ActiveSuspensionsIntegrationTest.java @@ -1,6 +1,5 @@ package uk.nhs.prm.repo.re_registration; -import com.amazonaws.services.sqs.AmazonSQSAsync; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -9,6 +8,7 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import software.amazon.awssdk.services.sqs.SqsClient; import uk.nhs.prm.repo.re_registration.data.ActiveSuspensionsDb; import uk.nhs.prm.repo.re_registration.infra.LocalStackAwsConfig; import uk.nhs.prm.repo.re_registration.model.ActiveSuspensionsMessage; @@ -27,7 +27,7 @@ @ContextConfiguration(classes = LocalStackAwsConfig.class) public class ActiveSuspensionsIntegrationTest { @Autowired - private AmazonSQSAsync amazonSQSAsync; + private SqsClient sqs; @Autowired ActiveSuspensionsDb activeSuspensionsDb; @@ -38,9 +38,6 @@ public class ActiveSuspensionsIntegrationTest { @Value("${aws.reRegistrationsQueueName}") private String reRegistrationsQueueName; - @Autowired - private AmazonSQSAsync sqs; - private static final String NHS_NUMBER = "0987654321"; private static final String PREVIOUS_ODS_CODE = "OLD001"; private static final String NEWLY_REGISTERED_ODS_CODE = "NEW001"; @@ -77,8 +74,8 @@ void shouldDeleteRecordFromActiveSuspensionsDbWhenRecordFoundByNhsNumber() { } private void sendMessage(String queueName, String messageBody) { - var queueUrl = amazonSQSAsync.getQueueUrl(queueName).getQueueUrl(); - amazonSQSAsync.sendMessage(queueUrl, messageBody); + var queueUrl = sqs.getQueueUrl(builder -> builder.queueName(queueName)).queueUrl(); + sqs.sendMessage(builder -> builder.queueUrl(queueUrl).messageBody(messageBody)); } private String getActiveSuspensionsMessage() { diff --git a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ReRegistrationsIntegrationTest.java b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ReRegistrationsIntegrationTest.java index c6b372dc..8d4de98d 100644 --- a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ReRegistrationsIntegrationTest.java +++ b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/ReRegistrationsIntegrationTest.java @@ -1,9 +1,5 @@ package uk.nhs.prm.repo.re_registration; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.CreateQueueRequest; -import com.amazonaws.services.sqs.model.Message; -import com.amazonaws.services.sqs.model.ReceiveMessageRequest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; @@ -12,6 +8,10 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.Message; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; import uk.nhs.prm.repo.re_registration.infra.LocalStackAwsConfig; import uk.nhs.prm.repo.re_registration.logging.TestLogAppender; import uk.nhs.prm.repo.re_registration.message_publishers.ReRegistrationAuditPublisher; @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -32,7 +33,7 @@ public class ReRegistrationsIntegrationTest { @Autowired - private AmazonSQSAsync amazonSQSAsync; + private SqsClient sqs; @Autowired ReRegistrationAuditPublisher publisher; @@ -40,18 +41,14 @@ public class ReRegistrationsIntegrationTest { @Value("${aws.reRegistrationsQueueName}") private String reRegistrationsQueueName; - private String getReRegistrationsEvent() { return new ReRegistrationEvent("1234567890", "ABC123", UUID.randomUUID().toString(), "2017-11-01T15:00:33+00:00").toJsonString(); } private void createQueue(String queueName) { - CreateQueueRequest createQueueRequest = new CreateQueueRequest(); - createQueueRequest.setQueueName(queueName); - HashMap attributes = new HashMap<>(); - createQueueRequest.withAttributes(attributes); - amazonSQSAsync.createQueue(reRegistrationsQueueName); - + sqs.createQueue(CreateQueueRequest.builder() + .queueName(queueName) + .build()); } @Test @@ -68,29 +65,24 @@ void shouldReceiveAndLogAndAcknowledgeReRegistrationsEvent() { assertThat(receiveLog).isNotNull(); }); - var messages = receiveMessages(reRegistrationsQueueName); - assertThat(messages).isEmpty(); } - private void sendMessage(String queueName, String messageBody) { var queueUrl = getQueueUrl(queueName); - amazonSQSAsync.sendMessage(queueUrl, messageBody); + sqs.sendMessage(builder -> builder.queueUrl(queueUrl).messageBody(messageBody)); } private String getQueueUrl(String queueName) { - return amazonSQSAsync.getQueueUrl(queueName).getQueueUrl(); + return sqs.getQueueUrl(builder -> builder.queueName(queueName)).queueUrl(); } - @Test private List receiveMessages(String queueName) { - String queueUrl = amazonSQSAsync.getQueueUrl(queueName).getQueueUrl(); - - var receiveMessageRequest = new ReceiveMessageRequest().withQueueUrl(queueUrl); - return amazonSQSAsync.receiveMessage(receiveMessageRequest).getMessages(); + String queueUrl = getQueueUrl(queueName); + var receiveMessageRequest = ReceiveMessageRequest.builder() + .queueUrl(queueUrl) + .build(); + return sqs.receiveMessage(receiveMessageRequest).messages(); } - } - diff --git a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/delete_ehr/DeleteEhrIntegrationTest.java b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/delete_ehr/DeleteEhrIntegrationTest.java index 1d9afe75..68982e30 100644 --- a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/delete_ehr/DeleteEhrIntegrationTest.java +++ b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/delete_ehr/DeleteEhrIntegrationTest.java @@ -1,9 +1,5 @@ package uk.nhs.prm.repo.re_registration.delete_ehr; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.Message; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; -import com.amazonaws.services.sqs.model.ReceiveMessageRequest; import com.github.tomakehurst.wiremock.WireMockServer; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -20,6 +16,10 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.Message; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; import uk.nhs.prm.repo.re_registration.data.ActiveSuspensionsDb; import uk.nhs.prm.repo.re_registration.infra.LocalStackAwsConfig; import uk.nhs.prm.repo.re_registration.model.ActiveSuspensionsMessage; @@ -30,17 +30,17 @@ import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.delete; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.get; import static com.github.tomakehurst.wiremock.client.WireMock.matching; -import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; -import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @SpringBootTest() @ActiveProfiles("test") @ExtendWith(SpringExtension.class) -@ContextConfiguration( classes = LocalStackAwsConfig.class) +@ContextConfiguration(classes = LocalStackAwsConfig.class) @DirtiesContext public class DeleteEhrIntegrationTest { @@ -48,7 +48,7 @@ public class DeleteEhrIntegrationTest { public static final String NHS_NUMBER = "9999567890"; @Autowired - private AmazonSQSAsync sqs; + private SqsClient sqs; @Autowired ActiveSuspensionsDb activeSuspensionsDb; @@ -66,12 +66,11 @@ public class DeleteEhrIntegrationTest { private String reRegistrationsQueueUrl; private String reRegistrationsAuditUrl; - @BeforeEach public void setUp() { stubPdsAdaptor = initializeWebServer(); - reRegistrationsQueueUrl = sqs.getQueueUrl(reRegistrationsQueueName).getQueueUrl(); - reRegistrationsAuditUrl = sqs.getQueueUrl(reRegistrationsAuditQueueName).getQueueUrl(); + reRegistrationsQueueUrl = sqs.getQueueUrl(builder -> builder.queueName(reRegistrationsQueueName)).queueUrl(); + reRegistrationsAuditUrl = sqs.getQueueUrl(builder -> builder.queueName(reRegistrationsAuditQueueName)).queueUrl(); stubResponses(); } @@ -79,7 +78,7 @@ public void setUp() { public void tearDown() { stubPdsAdaptor.resetAll(); stubPdsAdaptor.stop(); - sqs.purgeQueue(new PurgeQueueRequest(reRegistrationsAuditUrl)); + sqs.purgeQueue(PurgeQueueRequest.builder().queueUrl(reRegistrationsAuditUrl).build()); } private WireMockServer initializeWebServer() { @@ -91,10 +90,10 @@ private WireMockServer initializeWebServer() { @Test void shouldPutTheEHRDeleteAuditMessageOntoTheAuditQueueWhenActiveSuspensionExistsInDBAndPDSReturnsAStatusCode200() { activeSuspensionsDb.save(getActiveSuspensionsMessage()); - sqs.sendMessage(reRegistrationsQueueUrl, getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); System.out.println("Found message - " + messageBody); assertThat(messageBody).contains("\"status\":\"ACTION:RE_REGISTRATION_EHR_DELETED\""); @@ -103,13 +102,12 @@ void shouldPutTheEHRDeleteAuditMessageOntoTheAuditQueueWhenActiveSuspensionExist }); } - @Test void shouldPutTheUnknownReRegistrationsAuditMessageOntoTheAuditQueueWhenActiveSuspensionDoesNotExistInDBAndPDSReturnsAStatusCode200() { - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); System.out.println("Found message - " + messageBody); assertThat(messageBody).contains("\"status\":\"NO_ACTION:UNKNOWN_REGISTRATION_EVENT_RECEIVED\""); }); @@ -121,17 +119,16 @@ private void stubResponses() { } private void setPds200SuccessState() { - stubFor(get(urlMatching("/suspended-patient-status/" + NHS_NUMBER)) - .withHeader("Authorization", matching("Basic cmUtcmVnaXN0cmF0aW9uLXNlcnZpY2U6ZGVmYXVsdA==")) - .inScenario("Retry Scenario") + stubPdsAdaptor.stubFor(get(urlEqualTo("/suspended-patient-status/" + NHS_NUMBER)) + .withHeader("Authorization", equalTo("Basic cmUtcmVnaXN0cmF0aW9uLXNlcnZpY2U6ZGVmYXVsdA==")) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "text/xml") + .withHeader("Content-Type", "application/json") .withBody(getPdsResponseString().getBody()))); } private void ehrRepository200Response() { - stubFor(delete(urlMatching("/patients/" + NHS_NUMBER)) + stubPdsAdaptor.stubFor(delete(urlEqualTo("/patients/" + NHS_NUMBER)) .withHeader("Authorization", matching(authKey)) .willReturn(aResponse() .withStatus(200) @@ -151,13 +148,14 @@ private ReRegistrationEvent getReRegistrationEvent() { private List checkMessageInRelatedQueue(String queueUrl) { LOGGER.info("Checking SQS Queue: {}", queueUrl); - ReceiveMessageRequest requestForMessagesWithAttributes = new ReceiveMessageRequest() - .withQueueUrl(queueUrl) - .withMessageAttributeNames("traceId"); + ReceiveMessageRequest requestForMessagesWithAttributes = ReceiveMessageRequest.builder() + .queueUrl(queueUrl) + .messageAttributeNames("traceId") + .build(); final List messages = sqs .receiveMessage(requestForMessagesWithAttributes) - .getMessages(); + .messages(); LOGGER.info("Found {} messages on queue: {}", messages.size(), queueUrl); assertThat(messages).hasSize(1); diff --git a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/infra/LocalStackAwsConfig.java b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/infra/LocalStackAwsConfig.java index 4c1fb570..2b3c0357 100644 --- a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/infra/LocalStackAwsConfig.java +++ b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/infra/LocalStackAwsConfig.java @@ -1,13 +1,5 @@ package uk.nhs.prm.repo.re_registration.infra; -import com.amazonaws.auth.AWSStaticCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.client.builder.AwsClientBuilder; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; -import com.amazonaws.services.sqs.model.CreateQueueRequest; -import com.amazonaws.services.sqs.model.GetQueueAttributesResult; -import com.amazonaws.services.sqs.model.QueueDoesNotExistException; import jakarta.annotation.PostConstruct; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -32,9 +24,12 @@ import software.amazon.awssdk.services.sns.model.CreateTopicResponse; import software.amazon.awssdk.services.sns.model.SubscribeRequest; import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.CreateQueueRequest; +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; +import software.amazon.awssdk.services.sqs.model.QueueDoesNotExistException; import java.net.URI; -import java.net.URISyntaxException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -60,7 +55,7 @@ public class LocalStackAwsConfig { private String awsRegion; @Autowired - private AmazonSQSAsync amazonSQSAsync; + private SqsClient sqsClient; @Autowired private DynamoDbClient dynamoDbClient; @@ -69,18 +64,11 @@ public class LocalStackAwsConfig { private String activeSuspensionsDynamoDbTableName; @Bean - public static SqsClient sqsClient(@Value("${localstack.url}") String localstackUrl) throws URISyntaxException { + public static SqsClient sqsClient(@Value("${localstack.url}") String localstackUrl) { return SqsClient.builder() - .credentialsProvider((() -> AwsBasicCredentials.create("FAKE", "FAKE"))) - .endpointOverride(new URI(localstackUrl)) - .build(); - } - - @Bean - public static AmazonSQSAsync amazonSQSAsync(@Value("${localstack.url}") String localstackUrl) { - return AmazonSQSAsyncClientBuilder.standard() - .withCredentials(new AWSStaticCredentialsProvider(new BasicAWSCredentials("FAKE", "FAKE"))) - .withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(localstackUrl, "eu-west-2")) + .credentialsProvider(StaticCredentialsProvider.create(AwsBasicCredentials.create("FAKE", "FAKE"))) + .endpointOverride(URI.create(localstackUrl)) + .region(Region.EU_WEST_2) .build(); } @@ -126,10 +114,12 @@ public String secretAccessKey() { @PostConstruct public void setupTestQueuesTopicsAndDb() { recreateReRegistrationsQueue(); - var reRegistrationsAuditQueue = amazonSQSAsync.createQueue(reRegistrationsAuditQueueName); + var reRegistrationsAuditQueue = sqsClient.createQueue( + CreateQueueRequest.builder().queueName(reRegistrationsAuditQueueName).build() + ); var topic = snsClient.createTopic(CreateTopicRequest.builder().name("re_registration_audit_sns_topic").build()); - createSnsTestReceiverSubscription(topic, getQueueArn(reRegistrationsAuditQueue.getQueueUrl())); + createSnsTestReceiverSubscription(topic, getQueueArn(reRegistrationsAuditQueue.queueUrl())); setupDbAndTable(); } @@ -186,11 +176,7 @@ private void recreateReRegistrationsQueue() { } private void createQueue(String queueName) { - CreateQueueRequest createQueueRequest = new CreateQueueRequest(); - createQueueRequest.setQueueName(queueName); - HashMap attributes = new HashMap<>(); - createQueueRequest.withAttributes(attributes); - amazonSQSAsync.createQueue(queueName); + sqsClient.createQueue(CreateQueueRequest.builder().queueName(queueName).build()); } private void createSnsTestReceiverSubscription(CreateTopicResponse topic, String queueArn) { @@ -215,12 +201,18 @@ private void ensureQueueDeleted(String queueName) { } private void deleteQueue(String queueName) { - amazonSQSAsync.deleteQueue(amazonSQSAsync.getQueueUrl(queueName).getQueueUrl()); + var queueUrl = sqsClient.getQueueUrl(builder -> builder.queueName(queueName)).queueUrl(); + sqsClient.deleteQueue(builder -> builder.queueUrl(queueUrl)); } private String getQueueArn(String queueUrl) { - GetQueueAttributesResult queueAttributes = amazonSQSAsync.getQueueAttributes(queueUrl, List.of("QueueArn")); - return queueAttributes.getAttributes().get("QueueArn"); + var queueAttributes = sqsClient.getQueueAttributes( + GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames(QueueAttributeName.QUEUE_ARN) + .build() + ); + return queueAttributes.attributes().get(QueueAttributeName.QUEUE_ARN); } } diff --git a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/PdsAdaptorServiceIntegrationTest.java b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/PdsAdaptorServiceIntegrationTest.java index 0f3f07df..6084cb77 100644 --- a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/PdsAdaptorServiceIntegrationTest.java +++ b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/PdsAdaptorServiceIntegrationTest.java @@ -1,10 +1,5 @@ package uk.nhs.prm.repo.re_registration.pds; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.GetQueueAttributesResult; -import com.amazonaws.services.sqs.model.Message; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; -import com.amazonaws.services.sqs.model.ReceiveMessageRequest; import com.github.tomakehurst.wiremock.WireMockServer; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -19,16 +14,27 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.GetQueueAttributesRequest; +import software.amazon.awssdk.services.sqs.model.Message; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.QueueAttributeName; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; import uk.nhs.prm.repo.re_registration.data.ActiveSuspensionsDb; import uk.nhs.prm.repo.re_registration.infra.LocalStackAwsConfig; import uk.nhs.prm.repo.re_registration.model.ActiveSuspensionsMessage; import uk.nhs.prm.repo.re_registration.model.ReRegistrationEvent; -import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; -import static com.github.tomakehurst.wiremock.client.WireMock.*; +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; +import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; +import static com.github.tomakehurst.wiremock.client.WireMock.verify; import static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -36,15 +42,16 @@ @SpringBootTest() @ActiveProfiles("test") @ExtendWith(SpringExtension.class) -@ContextConfiguration( classes = LocalStackAwsConfig.class) +@ContextConfiguration(classes = LocalStackAwsConfig.class) @DirtiesContext public class PdsAdaptorServiceIntegrationTest { public static final String NHS_NUMBER = "1234567890"; public static final String STATUS_MESSAGE_FOR_WHEN_PATIENT_IS_STILL_SUSPENDED = "NO_ACTION:RE_REGISTRATION_FAILED_STILL_SUSPENDED"; public static final String STATUS_MESSAGE_FOR_WHEN_PDS_RETURNS_4XX_ERROR = "NO_ACTION:RE_REGISTRATION_FAILED_PDS_ERROR"; + @Autowired - private AmazonSQSAsync sqs; + private SqsClient sqs; @Value("${aws.reRegistrationsQueueName}") private String reRegistrationsQueueName; @@ -52,7 +59,7 @@ public class PdsAdaptorServiceIntegrationTest { @Value("${aws.reRegistrationsAuditQueueName}") private String reRegistrationsAuditQueueName; - WireMockServer stubPdsAdaptor; + private WireMockServer stubPdsAdaptor; private String reRegistrationsQueueUrl; private String reRegistrationsAuditUrl; private String nemsMessageId = "nemsMessageId"; @@ -63,8 +70,8 @@ public class PdsAdaptorServiceIntegrationTest { @BeforeEach public void setUp() { stubPdsAdaptor = initializeWebServer(); - reRegistrationsQueueUrl = sqs.getQueueUrl(reRegistrationsQueueName).getQueueUrl(); - reRegistrationsAuditUrl = sqs.getQueueUrl(reRegistrationsAuditQueueName).getQueueUrl(); + reRegistrationsQueueUrl = sqs.getQueueUrl(builder -> builder.queueName(reRegistrationsQueueName)).queueUrl(); + reRegistrationsAuditUrl = sqs.getQueueUrl(builder -> builder.queueName(reRegistrationsAuditQueueName)).queueUrl(); activeSuspensionsDb.save(getActiveSuspensionsMessage()); } @@ -72,8 +79,8 @@ public void setUp() { public void tearDown() { stubPdsAdaptor.resetAll(); stubPdsAdaptor.stop(); - sqs.purgeQueue(new PurgeQueueRequest(reRegistrationsAuditUrl)); - sqs.purgeQueue(new PurgeQueueRequest(reRegistrationsQueueUrl)); + sqs.purgeQueue(PurgeQueueRequest.builder().queueUrl(reRegistrationsAuditUrl).build()); + sqs.purgeQueue(PurgeQueueRequest.builder().queueUrl(reRegistrationsQueueUrl).build()); } private WireMockServer initializeWebServer() { @@ -85,31 +92,31 @@ private WireMockServer initializeWebServer() { @Test void shouldRetryUpTo3TimesAndNotPutAnythingOnReRegistrationAuditTopicWhenPdsReturnsResponseWithStatusCode500() { setPdsRetryMessage(NHS_NUMBER); - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> assertThat(isQueueEmpty(reRegistrationsAuditUrl)).isTrue()); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> verify(3, getRequestedFor(urlMatching("/suspended-patient-status/" + NHS_NUMBER)))); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> assertThat(isQueueEmpty(reRegistrationsAuditUrl)).isTrue()); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> verify(3, getRequestedFor(urlMatching("/suspended-patient-status/" + NHS_NUMBER)))); } @Test void shouldRetryWhenPdsReturnsResponseWithStatusCode500AndPublishOnReRegistrationAuditTopicOnce200IsReturned() { setPdsRetryMessageAndSucceed(NHS_NUMBER); - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); - assertThat(messageBody).contains(STATUS_MESSAGE_FOR_WHEN_PATIENT_IS_STILL_SUSPENDED); - assertThat(messageBody).contains(nemsMessageId); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); + assertThat(messageBody).contains(STATUS_MESSAGE_FOR_WHEN_PATIENT_IS_STILL_SUSPENDED); + assertThat(messageBody).contains(nemsMessageId); }); } @Test void shouldPutTheAuditStatusMessageOnAuditTopicWhenPdsReturnsResponseWithStatusCode200() { setPds200SuccessState(null, 1, NHS_NUMBER); - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); assertThat(messageBody).contains(STATUS_MESSAGE_FOR_WHEN_PATIENT_IS_STILL_SUSPENDED); assertThat(messageBody).contains(nemsMessageId); }); @@ -118,34 +125,34 @@ void shouldPutTheAuditStatusMessageOnAuditTopicWhenPdsReturnsResponseWithStatusC @Test void shouldPutTheAuditStatusMessageOnAuditTopicWhenPdsReturnsResponseWithStatusCode400() { setPdsErrorState(null, null, 1, NHS_NUMBER, 400); - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); - assertThat(messageBody).contains(STATUS_MESSAGE_FOR_WHEN_PDS_RETURNS_4XX_ERROR); - assertThat(messageBody).contains(nemsMessageId); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); + assertThat(messageBody).contains(STATUS_MESSAGE_FOR_WHEN_PDS_RETURNS_4XX_ERROR); + assertThat(messageBody).contains(nemsMessageId); }); } private void setPds200SuccessState(String startingState, int priority, String nhsNumber) { - stubFor(get(urlMatching("/suspended-patient-status/" + nhsNumber)).atPriority(priority) - .withHeader("Authorization", matching("Basic cmUtcmVnaXN0cmF0aW9uLXNlcnZpY2U6ZGVmYXVsdA==")) + stubPdsAdaptor.stubFor(get(urlEqualTo("/suspended-patient-status/" + nhsNumber)).atPriority(priority) + .withHeader("Authorization", equalTo("Basic cmUtcmVnaXN0cmF0aW9uLXNlcnZpY2U6ZGVmYXVsdA==")) .inScenario("Retry Scenario") .whenScenarioStateIs(startingState) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "text/xml") + .withHeader("Content-Type", "application/json") .withBody(getPdsResponseString().getBody()))); } private void setPdsErrorState(String startingState, String finishedState, int priority, String nhsNumber, int statusCode) { - stubFor(get(urlMatching("/suspended-patient-status/" + nhsNumber)).atPriority(priority) - .withHeader("Authorization", matching("Basic cmUtcmVnaXN0cmF0aW9uLXNlcnZpY2U6ZGVmYXVsdA==")) + stubPdsAdaptor.stubFor(get(urlEqualTo("/suspended-patient-status/" + nhsNumber)).atPriority(priority) + .withHeader("Authorization", equalTo("Basic cmUtcmVnaXN0cmF0aW9uLXNlcnZpY2U6ZGVmYXVsdA==")) .inScenario("Retry Scenario") .whenScenarioStateIs(startingState) .willReturn(aResponse() .withStatus(statusCode) - .withHeader("Content-Type", "text/xml") + .withHeader("Content-Type", "application/json") .withBody("Some content")) .willSetStateTo(finishedState)); } @@ -167,24 +174,33 @@ private ReRegistrationEvent getReRegistrationEvent() { } private boolean isQueueEmpty(String queueUrl) { - List attributeList = new ArrayList<>(); - attributeList.add("ApproximateNumberOfMessagesNotVisible"); - attributeList.add("ApproximateNumberOfMessages"); - GetQueueAttributesResult getQueueAttributesResult = sqs.getQueueAttributes(queueUrl, attributeList); + var getQueueAttributesResponse = sqs.getQueueAttributes(GetQueueAttributesRequest.builder() + .queueUrl(queueUrl) + .attributeNames( + QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE, + QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES + ) + .build()); - var numberOfMessageNotVisible = Integer.valueOf(getQueueAttributesResult.getAttributes().get("ApproximateNumberOfMessagesNotVisible")); - var numberOfMessageVisible = Integer.valueOf(getQueueAttributesResult.getAttributes().get("ApproximateNumberOfMessages")); + int numberOfMessageNotVisible = Integer.parseInt( + getQueueAttributesResponse.attributes().get(QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES_NOT_VISIBLE) + ); + int numberOfMessageVisible = Integer.parseInt( + getQueueAttributesResponse.attributes().get(QueueAttributeName.APPROXIMATE_NUMBER_OF_MESSAGES) + ); - return (numberOfMessageVisible == 0 && numberOfMessageNotVisible == 0); + return numberOfMessageVisible == 0 && numberOfMessageNotVisible == 0; } private List checkMessageInRelatedQueue(String queueUrl) { System.out.println("checking sqs queue: " + queueUrl); - var requestForMessagesWithAttributes - = new ReceiveMessageRequest().withQueueUrl(queueUrl) - .withMessageAttributeNames("traceId"); - List messages = sqs.receiveMessage(requestForMessagesWithAttributes).getMessages(); + ReceiveMessageRequest requestForMessagesWithAttributes = ReceiveMessageRequest.builder() + .queueUrl(queueUrl) + .messageAttributeNames("traceId") + .build(); + + List messages = sqs.receiveMessage(requestForMessagesWithAttributes).messages(); System.out.printf("Found %s messages on queue: %s%n", messages.size(), queueUrl); assertThat(messages).hasSize(1); return messages; diff --git a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/ToggleCanNotSendEhrDeleteRequestTest.java b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/ToggleCanNotSendEhrDeleteRequestTest.java index 933b3071..8acb1451 100644 --- a/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/ToggleCanNotSendEhrDeleteRequestTest.java +++ b/services/re-registration-service/src/integration/java/uk/nhs/prm/repo/re_registration/pds/ToggleCanNotSendEhrDeleteRequestTest.java @@ -1,9 +1,5 @@ package uk.nhs.prm.repo.re_registration.pds; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.model.Message; -import com.amazonaws.services.sqs.model.PurgeQueueRequest; -import com.amazonaws.services.sqs.model.ReceiveMessageRequest; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -15,6 +11,10 @@ import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import software.amazon.awssdk.services.sqs.SqsClient; +import software.amazon.awssdk.services.sqs.model.Message; +import software.amazon.awssdk.services.sqs.model.PurgeQueueRequest; +import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest; import uk.nhs.prm.repo.re_registration.data.ActiveSuspensionsDb; import uk.nhs.prm.repo.re_registration.infra.LocalStackAwsConfig; import uk.nhs.prm.repo.re_registration.model.ActiveSuspensionsMessage; @@ -29,7 +29,7 @@ @SpringBootTest(properties = "toggle.canSendDeleteEhrRequest=false") @ActiveProfiles("test") @ExtendWith(SpringExtension.class) -@ContextConfiguration( classes = LocalStackAwsConfig.class) +@ContextConfiguration(classes = LocalStackAwsConfig.class) @DirtiesContext public class ToggleCanNotSendEhrDeleteRequestTest { @@ -37,7 +37,7 @@ public class ToggleCanNotSendEhrDeleteRequestTest { public static final String STATUS_FOR_RECEIVED_REGISTRATION_EVENT = "NO_ACTION:RE_REGISTRATION_EVENT_RECEIVED"; @Autowired - private AmazonSQSAsync sqs; + private SqsClient sqs; @Autowired private ActiveSuspensionsDb activeSuspensionsDb; @@ -52,26 +52,25 @@ public class ToggleCanNotSendEhrDeleteRequestTest { private String reRegistrationsAuditUrl; private String nemsMessageId = "nemsMessageId"; - @BeforeEach public void setUp() { - reRegistrationsQueueUrl = sqs.getQueueUrl(reRegistrationsQueueName).getQueueUrl(); - reRegistrationsAuditUrl = sqs.getQueueUrl(reRegistrationsAuditQueueName).getQueueUrl(); + reRegistrationsQueueUrl = sqs.getQueueUrl(builder -> builder.queueName(reRegistrationsQueueName)).queueUrl(); + reRegistrationsAuditUrl = sqs.getQueueUrl(builder -> builder.queueName(reRegistrationsAuditQueueName)).queueUrl(); } @AfterEach public void tearDown() { - sqs.purgeQueue(new PurgeQueueRequest(reRegistrationsAuditUrl)); + sqs.purgeQueue(PurgeQueueRequest.builder().queueUrl(reRegistrationsAuditUrl).build()); } @Test void shouldSendToAuditQueueAndNotProcessMessageWhenToggleIsFalseAndActiveSuspensionIsFound() { var activeSuspensionsMessage = new ActiveSuspensionsMessage(NHS_NUMBER, "previous-ods-code", "2017-11-01T15:00:33+00:00"); activeSuspensionsDb.save(activeSuspensionsMessage); - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); assertThat(messageBody).contains(STATUS_FOR_RECEIVED_REGISTRATION_EVENT); assertThat(messageBody).contains(nemsMessageId); }); @@ -79,10 +78,10 @@ void shouldSendToAuditQueueAndNotProcessMessageWhenToggleIsFalseAndActiveSuspens @Test void shouldSendUnknownMessageToAuditQueueAndNotProcessMessageWhenToggleIsFalseAndActiveSuspensionNotIsFound() { - sqs.sendMessage(reRegistrationsQueueUrl,getReRegistrationEvent().toJsonString()); + sqs.sendMessage(builder -> builder.queueUrl(reRegistrationsQueueUrl).messageBody(getReRegistrationEvent().toJsonString())); - await().atMost(20, TimeUnit.SECONDS).untilAsserted(()-> { - String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).getBody(); + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + String messageBody = checkMessageInRelatedQueue(reRegistrationsAuditUrl).get(0).body(); assertThat(messageBody).contains("NO_ACTION:UNKNOWN_REGISTRATION_EVENT_RECEIVED"); }); } @@ -94,13 +93,14 @@ private ReRegistrationEvent getReRegistrationEvent() { private List checkMessageInRelatedQueue(String queueUrl) { System.out.println("checking sqs queue: " + queueUrl); - var requestForMessagesWithAttributes - = new ReceiveMessageRequest().withQueueUrl(queueUrl) - .withMessageAttributeNames("traceId"); - List messages = sqs.receiveMessage(requestForMessagesWithAttributes).getMessages(); + ReceiveMessageRequest requestForMessagesWithAttributes = ReceiveMessageRequest.builder() + .queueUrl(queueUrl) + .messageAttributeNames("traceId") + .build(); + + List messages = sqs.receiveMessage(requestForMessagesWithAttributes).messages(); System.out.printf("Found %s messages on queue: %s%n", messages.size(), queueUrl); assertThat(messages).hasSize(1); return messages; } - } diff --git a/services/re-registration-service/src/integration/resources/logback-test.xml b/services/re-registration-service/src/integration/resources/logback-test.xml new file mode 100644 index 00000000..cc10d4a7 --- /dev/null +++ b/services/re-registration-service/src/integration/resources/logback-test.xml @@ -0,0 +1,17 @@ + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/SqsListenerSpringConfiguration.java b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/SqsListenerSpringConfiguration.java index a6da5a35..4787c556 100644 --- a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/SqsListenerSpringConfiguration.java +++ b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/SqsListenerSpringConfiguration.java @@ -4,8 +4,7 @@ import com.amazon.sqs.javamessaging.SQSConnection; import com.amazon.sqs.javamessaging.SQSConnectionFactory; import com.amazon.sqs.javamessaging.SQSSession; -import com.amazonaws.services.sqs.AmazonSQSAsync; -import com.amazonaws.services.sqs.AmazonSQSAsyncClientBuilder; +import software.amazon.awssdk.services.sqs.SqsClient; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -17,9 +16,9 @@ import uk.nhs.prm.repo.re_registration.listener.ReRegistrationsEventListener; import uk.nhs.prm.repo.re_registration.parser.ActiveSuspensionsParser; -import javax.jms.JMSException; -import javax.jms.MessageConsumer; -import javax.jms.Session; +import jakarta.jms.JMSException; +import jakarta.jms.MessageConsumer; +import jakarta.jms.Session; @Configuration @RequiredArgsConstructor @@ -38,13 +37,13 @@ public class SqsListenerSpringConfiguration { private final ActiveSuspensionsParser activeSuspensionsParser; @Bean - public AmazonSQSAsync amazonSQSAsync() { - return AmazonSQSAsyncClientBuilder.defaultClient(); + public SqsClient sqsClient() { + return SqsClient.create(); } @Bean - public SQSConnection createConnection(AmazonSQSAsync amazonSQSAsync) throws JMSException { - SQSConnectionFactory connectionFactory = new SQSConnectionFactory(new ProviderConfiguration(), amazonSQSAsync); + public SQSConnection createConnection(SqsClient sqsClient) throws JMSException { + SQSConnectionFactory connectionFactory = new SQSConnectionFactory(new ProviderConfiguration(), sqsClient); return connectionFactory.createConnection(); } diff --git a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/Tracer.java b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/Tracer.java index 244472ba..7a7555e3 100644 --- a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/Tracer.java +++ b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/config/Tracer.java @@ -5,8 +5,8 @@ import org.slf4j.MDC; import org.springframework.context.annotation.Configuration; -import javax.jms.JMSException; -import javax.jms.Message; +import jakarta.jms.JMSException; +import jakarta.jms.Message; import java.util.UUID; @Slf4j diff --git a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponse.java b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponse.java index 4c3a5ecc..5d8ff482 100644 --- a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponse.java +++ b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponse.java @@ -1,7 +1,6 @@ package uk.nhs.prm.repo.re_registration.ehr_repo; import com.fasterxml.jackson.annotation.JsonProperty; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,9 +9,6 @@ @NoArgsConstructor @AllArgsConstructor public class EhrDeleteResponse { - @JsonProperty("data") - @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public EhrDeleteResponseContent ehrDeleteResponseContent; - } diff --git a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponseContent.java b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponseContent.java index a3385cf6..d1acd142 100644 --- a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponseContent.java +++ b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/ehr_repo/EhrDeleteResponseContent.java @@ -1,6 +1,5 @@ package uk.nhs.prm.repo.re_registration.ehr_repo; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -11,10 +10,7 @@ @NoArgsConstructor @AllArgsConstructor public class EhrDeleteResponseContent { - @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public String type; - @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public String id; - @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public List conversationIds; } diff --git a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListener.java b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListener.java index f8d30ec1..34824fd5 100644 --- a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListener.java +++ b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListener.java @@ -6,9 +6,9 @@ import uk.nhs.prm.repo.re_registration.handlers.ActiveSuspensionsHandler; import uk.nhs.prm.repo.re_registration.parser.ActiveSuspensionsParser; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.TextMessage; +import jakarta.jms.Message; +import jakarta.jms.MessageListener; +import jakarta.jms.TextMessage; @Slf4j @RequiredArgsConstructor diff --git a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListener.java b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListener.java index 82bd707f..283495f8 100644 --- a/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListener.java +++ b/services/re-registration-service/src/main/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListener.java @@ -5,9 +5,9 @@ import uk.nhs.prm.repo.re_registration.config.Tracer; import uk.nhs.prm.repo.re_registration.handlers.ReRegistrationsRetryHandler; -import javax.jms.Message; -import javax.jms.MessageListener; -import javax.jms.TextMessage; +import jakarta.jms.Message; +import jakarta.jms.MessageListener; +import jakarta.jms.TextMessage; @Slf4j @RequiredArgsConstructor diff --git a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/config/TracerTest.java b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/config/TracerTest.java index beac6955..a9496e76 100644 --- a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/config/TracerTest.java +++ b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/config/TracerTest.java @@ -5,7 +5,7 @@ import org.junit.jupiter.api.Test; import org.slf4j.MDC; -import javax.jms.JMSException; +import jakarta.jms.JMSException; import java.util.UUID; import static org.assertj.core.api.Assertions.assertThat; diff --git a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListenerTest.java b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListenerTest.java index 2a4bdfd2..99eb9243 100644 --- a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListenerTest.java +++ b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ActiveSuspensionsMessageListenerTest.java @@ -11,7 +11,7 @@ import uk.nhs.prm.repo.re_registration.model.ActiveSuspensionsMessage; import uk.nhs.prm.repo.re_registration.parser.ActiveSuspensionsParser; -import javax.jms.JMSException; +import jakarta.jms.JMSException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; diff --git a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListenerTest.java b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListenerTest.java index ecb274db..5ff84d3f 100644 --- a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListenerTest.java +++ b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/listener/ReRegistrationsEventListenerTest.java @@ -9,7 +9,7 @@ import uk.nhs.prm.repo.re_registration.config.Tracer; import uk.nhs.prm.repo.re_registration.handlers.ReRegistrationsRetryHandler; -import javax.jms.JMSException; +import jakarta.jms.JMSException; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; diff --git a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/utility/TestDataUtility.java b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/utility/TestDataUtility.java index 27551aae..0a5b6d5d 100644 --- a/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/utility/TestDataUtility.java +++ b/services/re-registration-service/src/test/java/uk/nhs/prm/repo/re_registration/utility/TestDataUtility.java @@ -1,8 +1,7 @@ package uk.nhs.prm.repo.re_registration.utility; - -import org.joda.time.DateTime; -import wiremock.org.apache.commons.lang3.RandomStringUtils; +import java.time.Instant; +import java.util.UUID; public final class TestDataUtility { public static String UNESCAPED_HTML = ""; @@ -10,11 +9,11 @@ public final class TestDataUtility { public static String NHS_NUMBER = "9745812541"; public static String getRandomTimestamp() { - return DateTime.now().toDateTimeISO().toString(); + return Instant.now().toString(); } public static String getRandomOdsCode() { - return RandomStringUtils.randomAlphanumeric(6); + return UUID.randomUUID().toString().replace("-", "").substring(0, 6); } private TestDataUtility() { } diff --git a/services/re-registration-service/tasks b/services/re-registration-service/tasks index ad1858ad..c4c6d705 100755 --- a/services/re-registration-service/tasks +++ b/services/re-registration-service/tasks @@ -31,15 +31,9 @@ function download_util() { echo "$UTIL_FILEPATH" } -function fetch_redaction_utils() { - download_util $AWS_HELPERS_VERSION run-with-redaction.sh - download_util $AWS_HELPERS_VERSION redactor -} - AWS_HELPERS_FILE=$(download_util $AWS_HELPERS_VERSION aws-helpers) source $AWS_HELPERS_FILE - #################################### # Instance (Environment) Variables # #################################### @@ -51,19 +45,11 @@ function check_env { fi } -function set_image_tag() { - if [[ -z "${GO_DEPENDENCY_LABEL_APP}" ]]; then - export IMAGE_TAG=${GO_PIPELINE_LABEL:-$(git rev-parse HEAD | cut -c 1-8)} - else - export IMAGE_TAG=${GO_DEPENDENCY_LABEL_APP} - fi -} - function get_aws_account_id { AWS_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r .Account) } -function configure_envs { +function configure_local_envs { export LOCALSTACK_URL="http://localhost:4566" export AWS_REGION="eu-west-2" export AWS_DEFAULT_REGION="eu-west-2" @@ -79,21 +65,22 @@ function configure_sonar_environment_variable { export SONAR_TOKEN=$(_get_aws_ssm_secret "/repo/dev/output/re-registration-service/sonar_token") } -function destroy_localstack { - echo docker processes running: $(docker ps) - - DOCKER_CONTAINERS_ON_PORT=$(docker ps --filter publish=4566 -q) - - echo docker processes running on expected localstack port: $DOCKER_CONTAINERS_ON_PORT - - if [ -z "$DOCKER_CONTAINERS_ON_PORT" ]; then - echo no localstack nonsense going on, cool - else - echo trying to kill localstack... - docker stop $DOCKER_CONTAINERS_ON_PORT - fi +############# +# FUNCTIONS # +############# +function start_localstack { + echo "################################" + echo "##### Starting Localstack ######" + echo "################################" + docker-compose -f docker-compose.localstack-local.yaml up -d } +function stop_localstack { + echo "################################" + echo "##### Stopping Localstack ######" + echo "################################" + docker-compose -f docker-compose.localstack-local.yaml down +} ########### ## TASKS ## @@ -101,92 +88,50 @@ function destroy_localstack { command="$1" case "${command}" in - fetch_utils) - fetch_redaction_utils - ;; - destroy_localstack) - destroy_localstack + start_localstack) + configure_local_envs + start_localstack ;; - build_docker) - configure_envs - ./tasks build - configure_docker_repository_uri - fetch_redaction_utils - build_docker_image - echo "Pushing the Docker image... $REPOSITORY_URI:$IMAGE_TAG" - docker push $REPOSITORY_URI:$IMAGE_TAG - ;; - build) - rm -rf build/ - ./gradlew assemble + stop_localstack) + stop_localstack ;; test_unit) ./gradlew test ;; test_integration) - configure_envs - destroy_localstack + configure_local_envs + start_localstack ./gradlew --info integration + stop_localstack ;; test_coverage) - configure_envs - destroy_localstack + configure_local_envs + start_localstack ./gradlew jacocoTestCoverageVerification + stop_localstack ;; code_quality) - destroy_localstack + start_localstack ./gradlew check -x test -x integration + stop_localstack ;; test_all) - configure_envs + configure_local_envs + start_localstack ./gradlew test integration jacocoTestCoverageVerification check + stop_localstack ;; run_sonar) - configure_envs + configure_local_envs _assume_environment_role $NHS_ENVIRONMENT configure_sonar_environment_variable - destroy_localstack + start_localstack ./gradlew build sonar --info + stop_localstack ;; run_local) - configure_envs + configure_local_envs ./gradlew bootRun ;; - run_localstack_local) - docker-compose -f docker-compose.localstack-local.yaml up -d - ;; - tf) - check_env - _assume_environment_role $NHS_ENVIRONMENT - tf_init - bash - ;; - tf_plan) - check_env - _assume_environment_role $NHS_ENVIRONMENT - tf_plan $2 - ;; - tf_apply) - check_env - _assume_environment_role $NHS_ENVIRONMENT - tf_apply - ;; - promote_docker_image) - check_env - set_image_tag - promote_docker_image "$IMAGE_REPO_NAME:$IMAGE_TAG" "$NHS_ENVIRONMENT" - ;; - wait_ecs) - check_env - _assume_environment_role $NHS_ENVIRONMENT - aws ecs wait services-stable \ - --region $AWS_DEFAULT_REGION \ - --cluster $NHS_ENVIRONMENT-${NHS_SERVICE}-ecs-cluster \ - --service $NHS_ENVIRONMENT-${NHS_SERVICE} - ;; - *) - echo "Invalid command: '${command}'" - exit 1 - ;; esac set +e diff --git a/services/suspension-service/tasks b/services/suspension-service/tasks index 7d0a7396..113430aa 100755 --- a/services/suspension-service/tasks +++ b/services/suspension-service/tasks @@ -15,26 +15,6 @@ echo "AWS helper scripts version: $AWS_HELPERS_VERSION" # Shared utils # ########################### -function download_util() { - local UTIL_VERSION=$1 - local UTIL_FILENAME=$2 - - local UTIL_FILEPATH="utils/$UTIL_VERSION/$UTIL_FILENAME" - - mkdir -p "utils/$UTIL_VERSION" - if [[ ! -f $UTIL_FILEPATH ]];then - wget --quiet -O $UTIL_FILEPATH https://github.com/nhsconnect/prm-deductions-support-infra/releases/download/${UTIL_VERSION}/${UTIL_FILENAME} - fi - chmod +x $UTIL_FILEPATH - - echo "$UTIL_FILEPATH" -} - -function fetch_redaction_utils() { - download_util $AWS_HELPERS_VERSION run-with-redaction.sh - download_util $AWS_HELPERS_VERSION redactor -} - AWS_HELPERS_FILE="utils/$AWS_HELPERS_VERSION/aws-helpers" mkdir -p "utils/$AWS_HELPERS_VERSION" @@ -167,9 +147,6 @@ function destroy_localstack { command="$1" case "${command}" in - fetch_utils) - fetch_redaction_utils - ;; destroy_localstack) destroy_localstack ;;