From fc066d1ddc36687e81bf0f9f2ad13632320028f1 Mon Sep 17 00:00:00 2001
From: Reuben Roberts <reuben.roberts@hee.nhs.uk>
Date: Fri, 24 Mar 2023 16:11:34 +0000
Subject: [PATCH 1/9] feat: AWS xray tracing config

TIS21-4226
---
 .aws/task-definition-template.json            |  6 ++-
 README.md                                     |  6 +++
 build.gradle                                  |  7 ++-
 .../credentials/api/IssueResource.java        |  2 +
 .../credentials/api/VerifyResource.java       |  2 +
 .../config/AwsXrayConfiguration.java          | 40 ++++++++++++++++
 .../config/AwsXrayInterceptor.java            | 42 +++++++++++++++++
 .../credentials/service/GatewayService.java   |  2 +
 .../credentials/service/IssuanceService.java  |  2 +
 .../credentials/service/JwtService.java       |  2 +
 .../service/RevocationService.java            |  2 +
 .../service/VerificationService.java          |  2 +
 src/main/resources/application.yml            |  9 +++-
 .../config/AwsXrayConfigurationTest.java      | 47 +++++++++++++++++++
 14 files changed, 168 insertions(+), 3 deletions(-)
 create mode 100644 src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
 create mode 100644 src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
 create mode 100644 src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java

diff --git a/.aws/task-definition-template.json b/.aws/task-definition-template.json
index a924197e..689e5923 100644
--- a/.aws/task-definition-template.json
+++ b/.aws/task-definition-template.json
@@ -4,6 +4,10 @@
       "name": "tis-trainee-credentials",
       "image": "430723991443.dkr.ecr.eu-west-2.amazonaws.com/tis-trainee-credentials:latest",
       "secrets": [
+        {
+          "name": "AWS_XRAY_DAEMON_ADDRESS",
+          "valueFrom": "/tis/monitoring/xray/daemon-host"
+        },
         {
           "name": "DB_HOST",
           "valueFrom": "/tis/trainee/${environment}/db/host"
@@ -116,7 +120,7 @@
           "value": "eu-west-2"
         },
         {
-          "name": "SENTRY_ENVIRONMENT",
+          "name": "ENVIRONMENT",
           "value": "${environment}"
         }
       ]
diff --git a/README.md b/README.md
index c343c106..284752b3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,7 @@
 # TIS Trainee Credentials
 
 ## About
+
 This service issues and verifies trainee digital credentials.
 
 ## Developing
@@ -17,6 +18,7 @@ gradlew bootRun
 
 | Name                              | Description                                                                                                             | Default     |
 |-----------------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------|
+| AWS_XRAY_DAEMON_ADDRESS | The AWS XRay daemon host. | |
 | DB_HOST                           | The MongoDB host to connect to.                                                                                         | localhost   |
 | DB_PORT                           | The port to connect to MongoDB on.                                                                                      | 27017       |
 | DB_NAME                           | The name of the MongoDB database.                                                                                       | credentials |
@@ -38,12 +40,14 @@ gradlew bootRun
 
 The Gradle `test` task can be used to run automated tests and produce coverage
 reports.
+
 ```shell
 gradlew test
 ```
 
 The Gradle `check` lifecycle task can be used to run automated tests and also
 verify formatting conforms to the code style guidelines.
+
 ```shell
 gradlew check
 ```
@@ -55,7 +59,9 @@ gradlew bootBuildImage
 ```
 
 ## Versioning
+
 This project uses [Semantic Versioning](semver.org).
 
 ## License
+
 This project is license under [The MIT License (MIT)](LICENSE).
diff --git a/build.gradle b/build.gradle
index ca9c447c..5aa3821c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -10,7 +10,7 @@ plugins {
 }
 
 group = "uk.nhs.tis.trainee"
-version = "0.16.0"
+version = "0.17.0"
 
 configurations {
   compileOnly {
@@ -51,6 +51,10 @@ dependencies {
   implementation "io.awspring.cloud:spring-cloud-starter-aws"
   implementation "io.awspring.cloud:spring-cloud-aws-messaging"
 
+  //AWS X-ray
+  implementation "com.amazonaws:aws-xray-recorder-sdk-spring:2.13.0"
+  compileOnly "javax.servlet:javax.servlet-api:4.0.1"
+
   implementation "commons-codec:commons-codec:1.16.0"
 
   ext.jjwtVersion = "0.11.5"
@@ -61,6 +65,7 @@ dependencies {
   testImplementation "org.springframework.cloud:spring-cloud-starter-bootstrap"
   testImplementation "com.playtika.testcontainers:embedded-redis:2.3.2"
   testImplementation "org.testcontainers:junit-jupiter:1.18.3"
+  testImplementation "javax.servlet:javax.servlet-api:4.0.1"
 }
 
 dependencyManagement {
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java
index 3c960434..725ed0b6 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/IssueResource.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.api;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import java.net.URI;
 import java.util.Optional;
 import java.util.UUID;
@@ -50,6 +51,7 @@
 @Slf4j
 @RestController()
 @RequestMapping("/api/issue")
+@XRayEnabled
 public class IssueResource {
 
   private final IssuanceService service;
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
index 99a4d894..672b3852 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.api;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import jakarta.validation.Valid;
 import java.net.URI;
 import lombok.extern.slf4j.Slf4j;
@@ -46,6 +47,7 @@
 @RestController
 @RequestMapping("/api/verify")
 @Validated
+@XRayEnabled
 public class VerifyResource {
 
   private final VerificationService service;
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
new file mode 100644
index 00000000..8020c800
--- /dev/null
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
@@ -0,0 +1,40 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright 2023 Crown Copyright (Health Education England)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package uk.nhs.hee.tis.trainee.credentials.config;
+
+import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
+import javax.servlet.Filter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConditionalOnExpression("!T(org.springframework.util.StringUtils)"
+    + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')")
+public class AwsXrayConfiguration {
+
+  @Bean
+  public Filter tracingFilter(@Value("${application.environment}") String environment) {
+    return new AWSXRayServletFilter("tis-trainee-credentials-" + environment);
+  }
+}
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
new file mode 100644
index 00000000..ff52001a
--- /dev/null
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
@@ -0,0 +1,42 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright 2023 Crown Copyright (Health Education England)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package uk.nhs.hee.tis.trainee.credentials.config;
+
+import com.amazonaws.xray.spring.aop.AbstractXRayInterceptor;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
+import org.springframework.stereotype.Component;
+
+@Aspect
+@Component
+@ConditionalOnExpression("!T(org.springframework.util.StringUtils)"
+    + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')")
+public class AwsXrayInterceptor extends AbstractXRayInterceptor {
+
+  @Override
+  @Pointcut(
+      "@within(com.amazonaws.xray.spring.aop.XRayEnabled) && (bean(*Resource) || bean(*Service))")
+  public void xrayEnabledClasses() {
+
+  }
+}
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java
index 5bd9e267..272825f6 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/GatewayService.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.service;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.impl.DefaultClaims;
@@ -50,6 +51,7 @@
  */
 @Slf4j
 @Service
+@XRayEnabled
 public class GatewayService {
 
   private static final String CLIENT_ID = "client_id";
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java
index 4f0da24b..e58fed52 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/IssuanceService.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.service;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import io.jsonwebtoken.Claims;
 import java.net.URI;
 import java.time.Instant;
@@ -45,6 +46,7 @@
  */
 @Slf4j
 @Service
+@XRayEnabled
 public class IssuanceService {
 
   private final GatewayService gatewayService;
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java
index 684b1342..11459d43 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/JwtService.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.service;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import io.jsonwebtoken.Claims;
 import io.jsonwebtoken.JwtParser;
@@ -41,6 +42,7 @@
  */
 @Slf4j
 @Service
+@XRayEnabled
 public class JwtService {
 
   private final ObjectMapper mapper;
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java
index 88e6b2f7..a1a3779d 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/RevocationService.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.service;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import java.time.Instant;
 import java.util.List;
 import java.util.Optional;
@@ -39,6 +40,7 @@
  */
 @Slf4j
 @Service
+@XRayEnabled
 public class RevocationService {
 
   private final CredentialMetadataRepository credentialMetadataRepository;
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java
index a72fb0af..d106498b 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/service/VerificationService.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.service;
 
+import com.amazonaws.xray.spring.aop.XRayEnabled;
 import io.jsonwebtoken.Claims;
 import jakarta.servlet.http.HttpServletRequest;
 import java.net.URI;
@@ -45,6 +46,7 @@
  */
 @Slf4j
 @Service
+@XRayEnabled
 public class VerificationService {
 
   private static final String CREDENTIAL_PREFIX = "openid ";
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
index 09e427dc..e24430da 100644
--- a/src/main/resources/application.yml
+++ b/src/main/resources/application.yml
@@ -16,6 +16,7 @@ application:
       verification-request: ${REDIS_TTL_VERIFICATION_REQUEST:300}
       verified-session: ${REDIS_TTL_VERIFIED_SESSION:600}
       credential-metadata: ${REDIS_TTL_ISSUING_FLOW:600}
+  environment: ${ENVIRONMENT:local}
   gateway:
     host: https://${GATEWAY_HOST}
     organisation-id: ${GATEWAY_ORGANISATION_ID}
@@ -41,9 +42,15 @@ application:
   signature:
     secret-key: ${SIGNATURE_SECRET_KEY}
 
+com:
+  amazonaws:
+    xray:
+      emitters:
+        daemon-address: ${AWS_XRAY_DAEMON_ADDRESS:}
+
 sentry:
   dsn: ${SENTRY_DSN:}
-  environment: ${SENTRY_ENVIRONMENT:}
+  environment: ${application.environment}
 
 spring:
   data:
diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
new file mode 100644
index 00000000..32c721db
--- /dev/null
+++ b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
@@ -0,0 +1,47 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright 2023 Crown Copyright (Health Education England)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package uk.nhs.hee.tis.trainee.credentials.config;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
+import javax.servlet.Filter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class AwsXrayConfigurationTest {
+
+  private AwsXrayConfiguration configuration;
+
+  @BeforeEach
+  void setUp() {
+    configuration = new AwsXrayConfiguration();
+  }
+
+  @Test
+  void shouldCreateInstanceOfAwsXrayServletFilter() {
+    Filter filter = configuration.tracingFilter("testEnvironment");
+
+    assertThat("Unexpected filter type.", filter, instanceOf(AWSXRayServletFilter.class));
+  }
+}

From 3ea6d5333d053f29921e652bcf82302d6f6b186f Mon Sep 17 00:00:00 2001
From: Reuben Roberts <reuben.roberts@hee.nhs.uk>
Date: Fri, 24 Mar 2023 16:22:16 +0000
Subject: [PATCH 2/9] fix: add test environment var

---
 src/test/resources/application.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml
index be0b182e..9cc7b910 100644
--- a/src/test/resources/application.yml
+++ b/src/test/resources/application.yml
@@ -5,5 +5,6 @@ application:
       verification-request: 60
       verified-session: 60
       credential-metadata: 60
+  environment: local
   signature:
     secret-key: test-secret-key

From 500026021b41a9cd2fe05d7b8b5f3fe60b25de2e Mon Sep 17 00:00:00 2001
From: Reuben Roberts <reuben.roberts@hee.nhs.uk>
Date: Fri, 24 Mar 2023 16:35:53 +0000
Subject: [PATCH 3/9] fix: javadocs

---
 .../tis/trainee/credentials/config/AwsXrayConfiguration.java   | 3 +++
 .../hee/tis/trainee/credentials/config/AwsXrayInterceptor.java | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
index 8020c800..1915b58c 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
@@ -28,6 +28,9 @@
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
+/**
+ * Configure the AWS Xray filter with the segment name set to service and environment.
+ */
 @Configuration
 @ConditionalOnExpression("!T(org.springframework.util.StringUtils)"
     + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')")
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
index ff52001a..c1f0f166 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
@@ -27,6 +27,9 @@
 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.stereotype.Component;
 
+/**
+ * Extend the Xray interceptor with instructions to wrap annotated Resource and Service beans.
+ */
 @Aspect
 @Component
 @ConditionalOnExpression("!T(org.springframework.util.StringUtils)"

From 43c1a544f7f375f372b06084e9a3fa2ed3772d95 Mon Sep 17 00:00:00 2001
From: Reuben Roberts <reuben.roberts@hee.nhs.uk>
Date: Mon, 27 Mar 2023 14:48:59 +0100
Subject: [PATCH 4/9] fix: readme variable name

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 284752b3..b16dd616 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ gradlew bootRun
 | GATEWAY_ISSUING_REDIRECT_URI      | Where the gateway issue should redirect to after issuing.                                                               |             |
 | GATEWAY_VERIFICATION_REDIRECT_URI | Where the gateway issue should redirect to verify a supplied credential, e.g. `<host>/api/credentials/verify/callback`. |             |
 | SENTRY_DSN                        | A Sentry error monitoring Data Source Name.                                                                             |             |
-| SENTRY_ENVIRONMENT                | The environment to log Sentry events against.                                                                           | local       |
+| ENVIRONMENT                | The environment to log Sentry events against.                                                                           | local       |
 | SIGNATURE_SECRET_KEY              | The secret key used to validate signed data.                                                                            |             |
 
 #### Usage Examples

From 150ef7928fe4c74fd2da1755612f473c283ffc9b Mon Sep 17 00:00:00 2001
From: Reuben Roberts <reuben.roberts@hee.nhs.uk>
Date: Tue, 28 Mar 2023 11:13:38 +0100
Subject: [PATCH 5/9] fix: build class introspection error

---
 README.md    | 2 +-
 build.gradle | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index b16dd616..e386e660 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ gradlew bootRun
 | DB_NAME                           | The name of the MongoDB database.                                                                                       | credentials |
 | DB_USER                           | The username to access the MongoDB instance.                                                                            | admin       |
 | DB_PASSWORD                       | The password to access the MongoDB instance.                                                                            | pwd         |
+| ENVIRONMENT                | The environment to log events against.                                                                           | local       |
 | GATEWAY_HOST                      | The credential gateway host.                                                                                            |             |
 | GATEWAY_CLIENT_ID                 | The client ID for the credential gateway.                                                                               |             |
 | GATEWAY_CLIENT_SECRET             | The client secret for the credential gateway.                                                                           |             |
@@ -31,7 +32,6 @@ gradlew bootRun
 | GATEWAY_ISSUING_REDIRECT_URI      | Where the gateway issue should redirect to after issuing.                                                               |             |
 | GATEWAY_VERIFICATION_REDIRECT_URI | Where the gateway issue should redirect to verify a supplied credential, e.g. `<host>/api/credentials/verify/callback`. |             |
 | SENTRY_DSN                        | A Sentry error monitoring Data Source Name.                                                                             |             |
-| ENVIRONMENT                | The environment to log Sentry events against.                                                                           | local       |
 | SIGNATURE_SECRET_KEY              | The secret key used to validate signed data.                                                                            |             |
 
 #### Usage Examples
diff --git a/build.gradle b/build.gradle
index 5aa3821c..c7cdfb2f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -53,7 +53,7 @@ dependencies {
 
   //AWS X-ray
   implementation "com.amazonaws:aws-xray-recorder-sdk-spring:2.13.0"
-  compileOnly "javax.servlet:javax.servlet-api:4.0.1"
+  implementation "javax.servlet:javax.servlet-api:4.0.1"
 
   implementation "commons-codec:commons-codec:1.16.0"
 

From 9ee8a622aeb59f45820a87416864f333feeda4c0 Mon Sep 17 00:00:00 2001
From: Reuben Roberts <reuben.roberts@hee.nhs.uk>
Date: Tue, 28 Mar 2023 11:52:54 +0100
Subject: [PATCH 6/9] feat: demo of 'working' trace

---
 README.md                                     |  8 +---
 .../credentials/api/VerifyResource.java       |  3 ++
 .../config/AwsXrayConfigurationTest.java      | 42 ++++++++++++++++---
 3 files changed, 41 insertions(+), 12 deletions(-)

diff --git a/README.md b/README.md
index e386e660..d5b4eb77 100644
--- a/README.md
+++ b/README.md
@@ -18,13 +18,13 @@ gradlew bootRun
 
 | Name                              | Description                                                                                                             | Default     |
 |-----------------------------------|-------------------------------------------------------------------------------------------------------------------------|-------------|
-| AWS_XRAY_DAEMON_ADDRESS | The AWS XRay daemon host. | |
+| AWS_XRAY_DAEMON_ADDRESS           | The AWS XRay daemon host.                                                                                               |             |
 | DB_HOST                           | The MongoDB host to connect to.                                                                                         | localhost   |
 | DB_PORT                           | The port to connect to MongoDB on.                                                                                      | 27017       |
 | DB_NAME                           | The name of the MongoDB database.                                                                                       | credentials |
 | DB_USER                           | The username to access the MongoDB instance.                                                                            | admin       |
 | DB_PASSWORD                       | The password to access the MongoDB instance.                                                                            | pwd         |
-| ENVIRONMENT                | The environment to log events against.                                                                           | local       |
+| ENVIRONMENT                       | The environment to log events against.                                                                                  | local       |
 | GATEWAY_HOST                      | The credential gateway host.                                                                                            |             |
 | GATEWAY_CLIENT_ID                 | The client ID for the credential gateway.                                                                               |             |
 | GATEWAY_CLIENT_SECRET             | The client secret for the credential gateway.                                                                           |             |
@@ -40,14 +40,12 @@ gradlew bootRun
 
 The Gradle `test` task can be used to run automated tests and produce coverage
 reports.
-
 ```shell
 gradlew test
 ```
 
 The Gradle `check` lifecycle task can be used to run automated tests and also
 verify formatting conforms to the code style guidelines.
-
 ```shell
 gradlew check
 ```
@@ -59,9 +57,7 @@ gradlew bootBuildImage
 ```
 
 ## Versioning
-
 This project uses [Semantic Versioning](semver.org).
 
 ## License
-
 This project is license under [The MIT License (MIT)](LICENSE).
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
index 672b3852..bb0188bb 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
@@ -21,6 +21,7 @@
 
 package uk.nhs.hee.tis.trainee.credentials.api;
 
+import com.amazonaws.xray.AWSXRay;
 import com.amazonaws.xray.spring.aop.XRayEnabled;
 import jakarta.validation.Valid;
 import java.net.URI;
@@ -60,10 +61,12 @@ public class VerifyResource {
   ResponseEntity<String> verifyIdentity(@RequestHeader(HttpHeaders.AUTHORIZATION) String authToken,
       @RequestParam(required = false) String state,
       @Valid @RequestBody IdentityDataDto dto) {
+    AWSXRay.beginSegment("verify-identity"); //TODO figure out how to get rid of this
     log.info("Received request to start identity verification.");
     URI uri = service.startIdentityVerification(authToken, dto, state);
 
     log.info("Identity verification successfully started.");
+    AWSXRay.endSegment();
     return ResponseEntity.created(uri).body(uri.toString());
   }
 
diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
index 32c721db..45064224 100644
--- a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
+++ b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
@@ -23,25 +23,55 @@
 
 import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertAll;
 
 import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
-import javax.servlet.Filter;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
 
 class AwsXrayConfigurationTest {
 
-  private AwsXrayConfiguration configuration;
+  private static final String DAEMON_PROPERTY = "com.amazonaws.xray.emitters.daemon-address";
+
+  private ApplicationContextRunner runner;
 
   @BeforeEach
   void setUp() {
-    configuration = new AwsXrayConfiguration();
+    runner = new ApplicationContextRunner()
+        .withUserConfiguration(AwsXrayConfiguration.class);
+  }
+
+  @Test
+  void shouldDisableConfigIfDaemonAddressNotSet() {
+    runner
+        .withPropertyValues(DAEMON_PROPERTY + "=")
+        .run(context -> assertThat("Unexpected bean presence.",
+            context.containsBean("tracingFilter"), is(false)));
   }
 
   @Test
-  void shouldCreateInstanceOfAwsXrayServletFilter() {
-    Filter filter = configuration.tracingFilter("testEnvironment");
+  void shouldEnableConfigIfDaemonAddressSet() {
+    runner
+        .withPropertyValues(DAEMON_PROPERTY + "=https://localhost:1234")
+        .run(context -> assertAll(
+            () -> assertThat("Unexpected bean presence.",
+                context.containsBean("awsXrayConfiguration"), is(true)),
+            () -> assertThat("Unexpected bean type.", context.getBean("awsXrayConfiguration"),
+                instanceOf(AwsXrayConfiguration.class))
+        ));
+  }
 
-    assertThat("Unexpected filter type.", filter, instanceOf(AWSXRayServletFilter.class));
+  @Test
+  void shouldRegisterAwsXrayTracingFilterWhenConfigEnabled() {
+    runner
+        .withPropertyValues(DAEMON_PROPERTY + "=https://localhost:1234")
+        .run(context -> assertAll(
+            () -> assertThat("Unexpected bean presence.", context.containsBean("tracingFilter"),
+                is(true)),
+            () -> assertThat("Unexpected bean type.", context.getBean("tracingFilter"),
+                instanceOf(AWSXRayServletFilter.class))
+        ));
   }
 }

From 67863557b46725a0147e59019025411bcec61632 Mon Sep 17 00:00:00 2001
From: Andy Dingley <Judge40@users.noreply.github.com>
Date: Thu, 31 Aug 2023 21:34:40 +0100
Subject: [PATCH 7/9] fix: tracing filter registration

Update the AWS Xray SDK to `2.14.0`, which allows it to support Spring
6's Jakarta based Filters.
Move the tracing filter configuration to FilterConfiguration as the
tracing filter must be placed before the SignedDataFilter in the filter
chain. Failure to register them in this order will cause the trace to
fail for any unsigned data, as the SignedDataFilter returns an error
instead of continuing the filter chain calls.

TIS21-4226
---
 build.gradle                                  |  4 +-
 .../config/AwsXrayConfiguration.java          | 43 -----------
 .../filter/FilterConfiguration.java           | 23 ++++++
 .../config/AwsXrayConfigurationTest.java      | 77 -------------------
 .../filter/FilterConfigurationTest.java       | 46 +++++++++++
 5 files changed, 70 insertions(+), 123 deletions(-)
 delete mode 100644 src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
 delete mode 100644 src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java

diff --git a/build.gradle b/build.gradle
index c7cdfb2f..126b94cd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -52,8 +52,7 @@ dependencies {
   implementation "io.awspring.cloud:spring-cloud-aws-messaging"
 
   //AWS X-ray
-  implementation "com.amazonaws:aws-xray-recorder-sdk-spring:2.13.0"
-  implementation "javax.servlet:javax.servlet-api:4.0.1"
+  implementation "com.amazonaws:aws-xray-recorder-sdk-spring:2.14.0"
 
   implementation "commons-codec:commons-codec:1.16.0"
 
@@ -65,7 +64,6 @@ dependencies {
   testImplementation "org.springframework.cloud:spring-cloud-starter-bootstrap"
   testImplementation "com.playtika.testcontainers:embedded-redis:2.3.2"
   testImplementation "org.testcontainers:junit-jupiter:1.18.3"
-  testImplementation "javax.servlet:javax.servlet-api:4.0.1"
 }
 
 dependencyManagement {
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
deleted file mode 100644
index 1915b58c..00000000
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfiguration.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright 2023 Crown Copyright (Health Education England)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- * associated documentation files (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge, publish, distribute,
- * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or
- * substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
- * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package uk.nhs.hee.tis.trainee.credentials.config;
-
-import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
-import javax.servlet.Filter;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-/**
- * Configure the AWS Xray filter with the segment name set to service and environment.
- */
-@Configuration
-@ConditionalOnExpression("!T(org.springframework.util.StringUtils)"
-    + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')")
-public class AwsXrayConfiguration {
-
-  @Bean
-  public Filter tracingFilter(@Value("${application.environment}") String environment) {
-    return new AWSXRayServletFilter("tis-trainee-credentials-" + environment);
-  }
-}
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java
index 69c85c54..4eb3b641 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfiguration.java
@@ -21,9 +21,13 @@
 
 package uk.nhs.hee.tis.trainee.credentials.filter;
 
+import com.amazonaws.xray.jakarta.servlet.AWSXRayServletFilter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
 
 /**
  * A configuration for request filters.
@@ -31,6 +35,23 @@
 @Configuration
 public class FilterConfiguration {
 
+  /**
+   * Configure the AWS Xray filter with the segment name set to service and environment.
+   *
+   * @param environment The environment to use.
+   * @return The {@link AWSXRayServletFilter} for the registration.
+   */
+  @Bean
+  @Order(0)
+  @ConditionalOnExpression("!T(org.springframework.util.StringUtils)"
+      + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')")
+  public FilterRegistrationBean<AWSXRayServletFilter> registerTracingFilter(
+      @Value("${application.environment}") String environment) {
+    FilterRegistrationBean<AWSXRayServletFilter> registrationBean = new FilterRegistrationBean<>();
+    registrationBean.setFilter(new AWSXRayServletFilter("tis-trainee-credentials-" + environment));
+    return registrationBean;
+  }
+
   /**
    * Register a {@link SignedDataFilter}.
    *
@@ -38,6 +59,7 @@ public class FilterConfiguration {
    * @return The {@link FilterRegistrationBean} for the registration.
    */
   @Bean
+  @Order(1)
   public FilterRegistrationBean<SignedDataFilter> registerSignedDataFilter(
       SignedDataFilter filter) {
     FilterRegistrationBean<SignedDataFilter> registrationBean = new FilterRegistrationBean<>();
@@ -55,6 +77,7 @@ public FilterRegistrationBean<SignedDataFilter> registerSignedDataFilter(
    * @return The {@link FilterRegistrationBean} for the registration.
    */
   @Bean
+  @Order(1)
   public FilterRegistrationBean<VerifiedSessionFilter> registerVerifiedSessionFilter(
       VerifiedSessionFilter filter) {
     FilterRegistrationBean<VerifiedSessionFilter> registrationBean = new FilterRegistrationBean<>();
diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
deleted file mode 100644
index 45064224..00000000
--- a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayConfigurationTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright 2023 Crown Copyright (Health Education England)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
- * associated documentation files (the "Software"), to deal in the Software without restriction,
- * including without limitation the rights to use, copy, modify, merge, publish, distribute,
- * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all copies or
- * substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
- * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- */
-
-package uk.nhs.hee.tis.trainee.credentials.config;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.junit.jupiter.api.Assertions.assertAll;
-
-import com.amazonaws.xray.javax.servlet.AWSXRayServletFilter;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.springframework.boot.test.context.runner.ApplicationContextRunner;
-
-class AwsXrayConfigurationTest {
-
-  private static final String DAEMON_PROPERTY = "com.amazonaws.xray.emitters.daemon-address";
-
-  private ApplicationContextRunner runner;
-
-  @BeforeEach
-  void setUp() {
-    runner = new ApplicationContextRunner()
-        .withUserConfiguration(AwsXrayConfiguration.class);
-  }
-
-  @Test
-  void shouldDisableConfigIfDaemonAddressNotSet() {
-    runner
-        .withPropertyValues(DAEMON_PROPERTY + "=")
-        .run(context -> assertThat("Unexpected bean presence.",
-            context.containsBean("tracingFilter"), is(false)));
-  }
-
-  @Test
-  void shouldEnableConfigIfDaemonAddressSet() {
-    runner
-        .withPropertyValues(DAEMON_PROPERTY + "=https://localhost:1234")
-        .run(context -> assertAll(
-            () -> assertThat("Unexpected bean presence.",
-                context.containsBean("awsXrayConfiguration"), is(true)),
-            () -> assertThat("Unexpected bean type.", context.getBean("awsXrayConfiguration"),
-                instanceOf(AwsXrayConfiguration.class))
-        ));
-  }
-
-  @Test
-  void shouldRegisterAwsXrayTracingFilterWhenConfigEnabled() {
-    runner
-        .withPropertyValues(DAEMON_PROPERTY + "=https://localhost:1234")
-        .run(context -> assertAll(
-            () -> assertThat("Unexpected bean presence.", context.containsBean("tracingFilter"),
-                is(true)),
-            () -> assertThat("Unexpected bean type.", context.getBean("tracingFilter"),
-                instanceOf(AWSXRayServletFilter.class))
-        ));
-  }
-}
diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java
index 067edf91..c86cfb83 100644
--- a/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java
+++ b/src/test/java/uk/nhs/hee/tis/trainee/credentials/filter/FilterConfigurationTest.java
@@ -22,19 +22,28 @@
 package uk.nhs.hee.tis.trainee.credentials.filter;
 
 import static org.hamcrest.CoreMatchers.hasItem;
+import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.sameInstance;
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.junit.jupiter.api.Assertions.assertAll;
 import static org.mockito.Mockito.mock;
 
+import com.amazonaws.xray.jakarta.servlet.AWSXRayServletFilter;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import java.util.Collection;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
 import uk.nhs.hee.tis.trainee.credentials.service.VerificationService;
 
 class FilterConfigurationTest {
 
+  private static final String DAEMON_PROPERTY = "com.amazonaws.xray.emitters.daemon-address";
+
   private static final String SIGNATURE_SECRET_KEY = "test-secret-key";
 
   FilterConfiguration configuration;
@@ -44,6 +53,43 @@ void setUp() {
     configuration = new FilterConfiguration();
   }
 
+  @Test
+  void shouldNotRegisterTracingFilterWhenDaemonAddressNotSet() {
+    ApplicationContextRunner runner = new ApplicationContextRunner()
+        .withUserConfiguration(FilterConfiguration.class)
+        .withBean(SignedDataFilter.class, () -> new SignedDataFilter(null, null, null))
+        .withBean(VerifiedSessionFilter.class, () -> new VerifiedSessionFilter(null));
+
+    runner
+        .withPropertyValues(DAEMON_PROPERTY + "=")
+        .run(context -> assertThat("Unexpected bean presence.",
+            context.containsBean("registerTracingFilter"), is(false)));
+  }
+
+  @Test
+  void shouldRegisterTracingFilterWhenDaemonAddressSet() {
+    ApplicationContextRunner runner = new ApplicationContextRunner()
+        .withUserConfiguration(FilterConfiguration.class)
+        .withBean(SignedDataFilter.class, () -> new SignedDataFilter(null, null, null))
+        .withBean(VerifiedSessionFilter.class, () -> new VerifiedSessionFilter(null));
+
+    runner
+        .withPropertyValues(DAEMON_PROPERTY + "=https://localhost:1234")
+        .run(context -> assertAll(
+            () -> assertThat("Unexpected bean presence.",
+                context.containsBean("registerTracingFilter"), is(true)),
+            () -> assertThat("Unexpected bean type.", context.getBean("registerTracingFilter"),
+                instanceOf(FilterRegistrationBean.class))
+        ));
+  }
+
+  @Test
+  void shouldRegisterTracingFilter() {
+    var registrationBean = configuration.registerTracingFilter("test");
+    AWSXRayServletFilter registeredFilter = registrationBean.getFilter();
+    assertThat("Unexpected registered filter.", registeredFilter, notNullValue());
+  }
+
   @Test
   void shouldRegisterSignedDataFilter() {
     SignedDataFilter filter = new SignedDataFilter(new ObjectMapper(), SIGNATURE_SECRET_KEY, null);

From c30a3cbb2f7d5d9d32a01c144bcc960b7ae3fe1c Mon Sep 17 00:00:00 2001
From: Andy Dingley <Judge40@users.noreply.github.com>
Date: Thu, 31 Aug 2023 21:48:21 +0100
Subject: [PATCH 8/9] feat: add ECS metadata to XRay traces

TIS21-4226
---
 .../config/AwsXrayInterceptor.java            |  27 +++++
 .../config/EcsMetadataConfiguration.java      |  30 +++++
 .../config/AwsXrayInterceptorTest.java        | 103 ++++++++++++++++++
 3 files changed, 160 insertions(+)
 create mode 100644 src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java

diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
index c1f0f166..bc76dc95 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptor.java
@@ -21,11 +21,18 @@
 
 package uk.nhs.hee.tis.trainee.credentials.config;
 
+import com.amazonaws.xray.entities.Subsegment;
 import com.amazonaws.xray.spring.aop.AbstractXRayInterceptor;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Map;
+import java.util.Optional;
+import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Aspect;
 import org.aspectj.lang.annotation.Pointcut;
 import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
 import org.springframework.stereotype.Component;
+import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata;
 
 /**
  * Extend the Xray interceptor with instructions to wrap annotated Resource and Service beans.
@@ -36,6 +43,26 @@
     + ".isEmpty('${com.amazonaws.xray.emitters.daemon-address}')")
 public class AwsXrayInterceptor extends AbstractXRayInterceptor {
 
+  private final EcsMetadata ecsMetadata;
+  private final ObjectMapper mapper;
+
+  AwsXrayInterceptor(Optional<EcsMetadata> ecsMetadata, ObjectMapper mapper) {
+    this.ecsMetadata = ecsMetadata.orElse(null);
+    this.mapper = mapper;
+  }
+
+  @Override
+  protected Map<String, Map<String, Object>> generateMetadata(ProceedingJoinPoint pjp,
+      Subsegment subsegment) {
+    Map<String, Map<String, Object>> metadata = super.generateMetadata(pjp, subsegment);
+
+    Map<String, Object> taskMetadataMap = mapper.convertValue(ecsMetadata, new TypeReference<>() {
+    });
+    metadata.put("EcsMetadata", taskMetadataMap);
+
+    return metadata;
+  }
+
   @Override
   @Pointcut(
       "@within(com.amazonaws.xray.spring.aop.XRayEnabled) && (bean(*Resource) || bean(*Service))")
diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java
index 53c4b5f3..8746aefc 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/config/EcsMetadataConfiguration.java
@@ -30,6 +30,9 @@
 import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.ContainerMetadata;
 import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.TaskMetadata;
 
+/**
+ * Configuration for retrieval of ECS metadata.
+ */
 @Configuration
 @ConditionalOnExpression("!T(org.springframework.util.StringUtils)"
     + ".isEmpty('${ecs.container.metadata.uri.v4:}')")
@@ -53,6 +56,12 @@ public EcsMetadata ecsMetadata(RestTemplate restTemplate,
     return new EcsMetadata(taskMetadata, containerMetadata);
   }
 
+  /**
+   * A representation of ECS metadata.
+   *
+   * @param taskMetadata      The ECS task metadata.
+   * @param containerMetadata The ECS container metadata.
+   */
   public record EcsMetadata(
       @JsonProperty("TaskMetadata")
       TaskMetadata taskMetadata,
@@ -60,6 +69,14 @@ public record EcsMetadata(
       @JsonProperty("ContainerMetadata")
       ContainerMetadata containerMetadata) {
 
+    /**
+     * A representation of ECS task metadata.
+     *
+     * @param cluster  The ECS cluster.
+     * @param taskArn  The running task's ARN.
+     * @param family   The task definition family.
+     * @param revision The task definition revision.
+     */
     record TaskMetadata(
         @JsonProperty("Cluster")
         String cluster,
@@ -75,6 +92,12 @@ record TaskMetadata(
 
     }
 
+    /**
+     * A representation of ECS container metadata.
+     *
+     * @param containerArn The ECS container ARN.
+     * @param logOptions   The container's log options.
+     */
     record ContainerMetadata(
 
         @JsonProperty("ContainerARN")
@@ -83,6 +106,13 @@ record ContainerMetadata(
         @JsonProperty("LogOptions")
         LogOptions logOptions) {
 
+      /**
+       * A representation of ECS container log options.
+       *
+       * @param logGroup  The container's log group.
+       * @param region    The container's log region.
+       * @param logStream The container's current log stream.
+       */
       record LogOptions(
           @JsonProperty("awslogs-group")
           String logGroup,
diff --git a/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java
new file mode 100644
index 00000000..31a850b2
--- /dev/null
+++ b/src/test/java/uk/nhs/hee/tis/trainee/credentials/config/AwsXrayInterceptorTest.java
@@ -0,0 +1,103 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright 2023 Crown Copyright (Health Education England)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+ * associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+ * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+package uk.nhs.hee.tis.trainee.credentials.config;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.amazonaws.xray.entities.Subsegment;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Map;
+import java.util.Optional;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata;
+import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.ContainerMetadata;
+import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.ContainerMetadata.LogOptions;
+import uk.nhs.hee.tis.trainee.credentials.config.EcsMetadataConfiguration.EcsMetadata.TaskMetadata;
+
+class AwsXrayInterceptorTest {
+
+  private ObjectMapper objectMapper;
+  private ProceedingJoinPoint pjp;
+  private Subsegment subsegment;
+
+  @BeforeEach
+  void setUp() {
+    objectMapper = new ObjectMapper();
+    subsegment = mock(Subsegment.class);
+
+    pjp = mock(ProceedingJoinPoint.class);
+    when(pjp.getTarget()).thenReturn(Object.class);
+  }
+
+  @Test
+  void shouldGenerateNullEcsMetadataWhenNotAvailable() {
+    AwsXrayInterceptor interceptor = new AwsXrayInterceptor(Optional.empty(), objectMapper);
+
+    Map<String, Map<String, Object>> metadata = interceptor.generateMetadata(pjp, subsegment);
+
+    assertThat("Unexpected X-Ray metadata.", metadata, notNullValue());
+
+    Map<String, Object> ecsMetadataMap = metadata.get("EcsMetadata");
+    assertThat("Unexpected ECS metadata.", ecsMetadataMap, nullValue());
+  }
+
+  @Test
+  void shouldGenerateEcsMetadataWhenAvailable() {
+    EcsMetadata ecsMetadata = new EcsMetadata(
+        new TaskMetadata("cluster", "taskArn", "family", "revision"),
+        new ContainerMetadata("containerArn", new LogOptions("logGroup", "region", "logStream")));
+    AwsXrayInterceptor interceptor = new AwsXrayInterceptor(Optional.of(ecsMetadata), objectMapper);
+
+    Map<String, Map<String, Object>> metadata = interceptor.generateMetadata(pjp, subsegment);
+
+    assertThat("Unexpected X-Ray metadata.", metadata, notNullValue());
+
+    Map<String, Object> ecsMetadataMap = metadata.get("EcsMetadata");
+    assertThat("Unexpected ECS metadata.", ecsMetadataMap, notNullValue());
+
+    Map<String, Object> taskMetadataMap = (Map<String, Object>) ecsMetadataMap.get("TaskMetadata");
+    assertThat("Unexpected task metadata.", taskMetadataMap, notNullValue());
+    assertThat("Unexpected cluster.", taskMetadataMap.get("Cluster"), is("cluster"));
+    assertThat("Unexpected task ARN.", taskMetadataMap.get("TaskARN"), is("taskArn"));
+    assertThat("Unexpected family.", taskMetadataMap.get("Family"), is("family"));
+
+    Map<String, Object> containerMetadataMap = (Map<String, Object>) ecsMetadataMap.get(
+        "ContainerMetadata");
+    assertThat("Unexpected container metadata.", containerMetadataMap, notNullValue());
+    assertThat("Unexpected container ARN.", containerMetadataMap.get("ContainerARN"),
+        is("containerArn"));
+
+    Map<String, Object> logOptionsMap = (Map<String, Object>) containerMetadataMap.get(
+        "LogOptions");
+    assertThat("Unexpected log options metadata.", logOptionsMap, notNullValue());
+    assertThat("Unexpected log group.", logOptionsMap.get("awslogs-group"), is("logGroup"));
+    assertThat("Unexpected log region.", logOptionsMap.get("awslogs-region"), is("region"));
+    assertThat("Unexpected log stream.", logOptionsMap.get("awslogs-stream"), is("logStream"));
+  }
+}

From 4b25b9f5f6381605e376cb853d4acbac4c140b99 Mon Sep 17 00:00:00 2001
From: Andy Dingley <Judge40@users.noreply.github.com>
Date: Thu, 31 Aug 2023 23:19:06 +0100
Subject: [PATCH 9/9] refactor: remove manual XRay segment

TIS21-4226
---
 .../uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
index bb0188bb..672b3852 100644
--- a/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
+++ b/src/main/java/uk/nhs/hee/tis/trainee/credentials/api/VerifyResource.java
@@ -21,7 +21,6 @@
 
 package uk.nhs.hee.tis.trainee.credentials.api;
 
-import com.amazonaws.xray.AWSXRay;
 import com.amazonaws.xray.spring.aop.XRayEnabled;
 import jakarta.validation.Valid;
 import java.net.URI;
@@ -61,12 +60,10 @@ public class VerifyResource {
   ResponseEntity<String> verifyIdentity(@RequestHeader(HttpHeaders.AUTHORIZATION) String authToken,
       @RequestParam(required = false) String state,
       @Valid @RequestBody IdentityDataDto dto) {
-    AWSXRay.beginSegment("verify-identity"); //TODO figure out how to get rid of this
     log.info("Received request to start identity verification.");
     URI uri = service.startIdentityVerification(authToken, dto, state);
 
     log.info("Identity verification successfully started.");
-    AWSXRay.endSegment();
     return ResponseEntity.created(uri).body(uri.toString());
   }