diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/config/AppConfig.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/config/AppConfig.scala index 8cc7707..a06bcc1 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/config/AppConfig.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/config/AppConfig.scala @@ -30,7 +30,6 @@ class AppConfig @Inject()(config: Configuration, servicesConfig: ServicesConfig) val auditingEnabled: Boolean = config.get[Boolean]("auditing.enabled") val graphiteHost: String = config.get[String]("microservice.metrics.graphite.host") - val ccn2Url: String = config.get[String]("ccn2Url") val ccn2Username: String = config.get[String]("ccn2Username") val ccn2Password: String = config.get[String]("ccn2Password") diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnector.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnector.scala index 7347db2..a556b78 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnector.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnector.scala @@ -18,7 +18,7 @@ package uk.gov.hmrc.apiplatformoutboundsoap.connectors import play.api.http.HeaderNames.CONTENT_TYPE import play.api.{Logger, LoggerLike} -import uk.gov.hmrc.apiplatformoutboundsoap.config.AppConfig +import uk.gov.hmrc.apiplatformoutboundsoap.models.SoapRequest import uk.gov.hmrc.http.HttpReads.Implicits._ import uk.gov.hmrc.http.{HttpClient, _} @@ -26,17 +26,17 @@ import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} @Singleton -class OutboundConnector @Inject()(appConfig: AppConfig, httpClient: HttpClient) +class OutboundConnector @Inject()(httpClient: HttpClient) (implicit ec: ExecutionContext) extends HttpErrorFunctions { val logger: LoggerLike = Logger - def postMessage(message: String): Future[Int] = { + def postMessage(soapRequest: SoapRequest): Future[Int] = { implicit val hc: HeaderCarrier = HeaderCarrier().withExtraHeaders(CONTENT_TYPE -> "application/soap+xml") - httpClient.POSTString[HttpResponse](appConfig.ccn2Url, message) map { response => + httpClient.POSTString[HttpResponse](soapRequest.destinationUrl, soapRequest.soapEnvelope) map { response => if (!is2xx(response.status)) { - logger.warn(s"Attempted request to ${appConfig.ccn2Url} responded with HTTP response code ${response.status}") + logger.warn(s"Attempted request to ${soapRequest.destinationUrl} responded with HTTP response code ${response.status}") } response.status } diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala index 200b194..4448952 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala @@ -27,6 +27,7 @@ sealed trait OutboundSoapMessage { val globalId: UUID val messageId: Option[String] val soapMessage: String + val destinationUrl: String val status: SendingStatus val createDateTime: DateTime val notificationUrl: Option[String] @@ -52,6 +53,7 @@ object OutboundSoapMessage { case class SentOutboundSoapMessage(globalId: UUID, messageId: Option[String], soapMessage: String, + destinationUrl: String, createDateTime: DateTime, ccnHttpStatus: Int, notificationUrl: Option[String] = None) extends OutboundSoapMessage { @@ -61,6 +63,7 @@ case class SentOutboundSoapMessage(globalId: UUID, case class FailedOutboundSoapMessage(globalId: UUID, messageId: Option[String], soapMessage: String, + destinationUrl: String, createDateTime: DateTime, ccnHttpStatus: Int, notificationUrl: Option[String] = None) extends OutboundSoapMessage { @@ -70,14 +73,15 @@ case class FailedOutboundSoapMessage(globalId: UUID, case class RetryingOutboundSoapMessage(globalId: UUID, messageId: Option[String], soapMessage: String, + destinationUrl: String, createDateTime: DateTime, retryDateTime: DateTime, ccnHttpStatus: Int, notificationUrl: Option[String] = None) extends OutboundSoapMessage { override val status: SendingStatus = SendingStatus.RETRYING - def toFailed = FailedOutboundSoapMessage(globalId, messageId, soapMessage, createDateTime, ccnHttpStatus, notificationUrl) - def toSent = SentOutboundSoapMessage(globalId, messageId, soapMessage, createDateTime, ccnHttpStatus, notificationUrl) + def toFailed = FailedOutboundSoapMessage(globalId, messageId, soapMessage, destinationUrl, createDateTime, ccnHttpStatus, notificationUrl) + def toSent = SentOutboundSoapMessage(globalId, messageId, soapMessage, destinationUrl, createDateTime, ccnHttpStatus, notificationUrl) } sealed trait SendingStatus extends EnumEntry diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/SoapRequest.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/SoapRequest.scala new file mode 100644 index 0000000..dab9590 --- /dev/null +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/SoapRequest.scala @@ -0,0 +1,19 @@ +/* + * Copyright 2021 HM Revenue & Customs + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package uk.gov.hmrc.apiplatformoutboundsoap.models + +case class SoapRequest(soapEnvelope: String, destinationUrl: String) diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala index b6177bb..ec22e28 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala @@ -21,7 +21,7 @@ import akka.stream.scaladsl.Source import org.joda.time.DateTime import org.joda.time.DateTime.now import org.joda.time.DateTimeZone.UTC -import play.api.libs.json.Json +import play.api.libs.json.{JsObject, Json} import play.modules.reactivemongo.ReactiveMongoComponent import reactivemongo.akkastream.cursorProducer import reactivemongo.api.ReadPreference @@ -64,7 +64,7 @@ class OutboundMessageRepository @Inject()(mongoComponent: ReactiveMongoComponent collection .find(Json.obj("status" -> SendingStatus.RETRYING.entryName, - "retryDateTime" -> Json.obj("$lte" -> now(UTC))), Option.empty[OutboundSoapMessage]) + "retryDateTime" -> Json.obj("$lte" -> now(UTC))), Option.empty[JsObject]) .sort(Json.obj("retryDateTime" -> 1)) .cursor[RetryingOutboundSoapMessage](ReadPreference.primaryPreferred) .documentSource() diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala index ad423ac..8d12a77 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala @@ -39,8 +39,9 @@ import uk.gov.hmrc.http.{HeaderCarrier, HttpErrorFunctions, NotFoundException} import java.util.UUID import javax.inject.{Inject, Singleton} +import javax.wsdl.extensions.soap12.SOAP12Address import javax.wsdl.xml.WSDLReader -import javax.wsdl.{Definition, Operation, Part, PortType} +import javax.wsdl.{Definition, Operation, Part, PortType, Service, Port} import javax.xml.namespace.QName import scala.collection.JavaConverters._ import scala.concurrent.{ExecutionContext, Future} @@ -61,9 +62,9 @@ class OutboundService @Inject()(outboundConnector: OutboundConnector, def sendMessage(message: MessageRequest): Future[OutboundSoapMessage] = { for { - envelope <- buildEnvelope(message) - result <- outboundConnector.postMessage(envelope) - outboundSoapMessage = buildOutboundSoapMessage(message, envelope, result) + soapRequest <- buildSoapRequest(message) + result <- outboundConnector.postMessage(soapRequest) + outboundSoapMessage = buildOutboundSoapMessage(message, soapRequest, result) _ <- outboundMessageRepository.persist(outboundSoapMessage) } yield outboundSoapMessage } @@ -74,7 +75,7 @@ class OutboundService @Inject()(outboundConnector: OutboundConnector, private def retryMessage(message: RetryingOutboundSoapMessage)(implicit hc: HeaderCarrier): Future[Unit] = { val nextRetryDateTime: DateTime = now.plus(appConfig.retryInterval.toMillis) - outboundConnector.postMessage(message.soapMessage) flatMap { result => + outboundConnector.postMessage(SoapRequest(message.soapMessage, message.destinationUrl)) flatMap { result => if (is2xx(result)) { logger.info(s"Retrying message with global ID ${message.globalId} and message ID ${message.messageId} succeeded") outboundMessageRepository.updateStatus(message.globalId, SendingStatus.SENT) map { updatedMessage => @@ -96,19 +97,20 @@ class OutboundService @Inject()(outboundConnector: OutboundConnector, } } - private def buildOutboundSoapMessage(message: MessageRequest, envelope: String, result: Int): OutboundSoapMessage = { + private def buildOutboundSoapMessage(message: MessageRequest, soapRequest: SoapRequest, result: Int): OutboundSoapMessage = { val globalId: UUID = randomUUID val messageId = message.addressing.flatMap(_.messageId) if (is2xx(result)) { logger.info(s"Message with global ID $globalId and message ID $messageId successfully sent") - SentOutboundSoapMessage(globalId, messageId, envelope, now, result, message.notificationUrl) + SentOutboundSoapMessage(globalId, messageId, soapRequest.soapEnvelope, soapRequest.destinationUrl , now, result, message.notificationUrl) } else { logger.info(s"Message with global ID $globalId and message ID $messageId failed on first attempt") - RetryingOutboundSoapMessage(globalId, messageId, envelope, now, now.plus(appConfig.retryInterval.toMillis), result, message.notificationUrl) + RetryingOutboundSoapMessage(globalId, messageId, soapRequest.soapEnvelope, soapRequest.destinationUrl, now, + now.plus(appConfig.retryInterval.toMillis), result, message.notificationUrl) } } - private def buildEnvelope(message: MessageRequest): Future[String] = { + private def buildSoapRequest(message: MessageRequest): Future[SoapRequest] = { cache.getOrElseUpdate[Definition](message.wsdlUrl, appConfig.cacheDuration) { parseWsdl(message.wsdlUrl) } map { wsdlDefinition: Definition => @@ -119,7 +121,12 @@ class OutboundService @Inject()(outboundConnector: OutboundConnector, val envelope: SOAPEnvelope = getSOAP12Factory.getDefaultEnvelope addHeaders(message, operation, envelope) addBody(message, operation, envelope) - wsSecurityService.addUsernameToken(envelope) + val enrichedEnvelope: String = wsSecurityService.addUsernameToken(envelope) + val url: String = wsdlDefinition.getAllServices.asScala.values.head.asInstanceOf[Service] + .getPorts.asScala.values.head.asInstanceOf[Port] + .getExtensibilityElements.asScala.filter(_.isInstanceOf[SOAP12Address]).head.asInstanceOf[SOAP12Address] + .getLocationURI + SoapRequest(enrichedEnvelope, url) } } diff --git a/conf/application.conf b/conf/application.conf index 37c0fbf..0c37fcc 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -110,7 +110,6 @@ mongodb { uri = "mongodb://localhost:27017/api-platform-outbound-soap" } -ccn2Url = "http://localhost:6704/destination/notifications" ccn2Username = "joe.bloggs" ccn2Password = "foobar" diff --git a/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/NotificationCallbackConnectorISpec.scala b/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/NotificationCallbackConnectorISpec.scala index ad2bb0d..f33d996 100644 --- a/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/NotificationCallbackConnectorISpec.scala +++ b/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/NotificationCallbackConnectorISpec.scala @@ -55,7 +55,7 @@ class NotificationCallbackConnectorISpec extends AnyWordSpec with Matchers with "sendNotification" should { "successfully send a status update to the caller's notification URL" in new Setup { val message: OutboundSoapMessage = RetryingOutboundSoapMessage( - globalId, messageId, "foobar", now, now, httpStatus, Some(wireMockBaseUrlAsString)) + globalId, messageId, "foobar", "some url", now, now, httpStatus, Some(wireMockBaseUrlAsString)) val expectedStatus: Int = OK primeNotificationsEndpoint(expectedStatus) @@ -66,7 +66,7 @@ class NotificationCallbackConnectorISpec extends AnyWordSpec with Matchers with "not send a status update when notification URL is absent" in new Setup { val message: OutboundSoapMessage = RetryingOutboundSoapMessage( - globalId, messageId, "foobar", now, now, httpStatus, None) + globalId, messageId, "foobar", "some url", now, now, httpStatus, None) val result: Option[Int] = await(underTest.sendNotification(message)) @@ -75,7 +75,7 @@ class NotificationCallbackConnectorISpec extends AnyWordSpec with Matchers with "successfully send a status update with a body to the caller's notification URL" in new Setup { val message: OutboundSoapMessage = RetryingOutboundSoapMessage( - globalId, messageId, "foobar", now, now, httpStatus, Some(wireMockBaseUrlAsString)) + globalId, messageId, "foobar", "some url", now, now, httpStatus, Some(wireMockBaseUrlAsString)) val expectedStatus: Int = OK val expectedNotificationBody: String = Json.toJson(SoapMessageStatus.fromOutboundSoapMessage(message)).toString() primeNotificationsEndpoint(expectedStatus) @@ -86,7 +86,7 @@ class NotificationCallbackConnectorISpec extends AnyWordSpec with Matchers with "set the Content-Type header to application/json" in new Setup { val message: OutboundSoapMessage = RetryingOutboundSoapMessage( - globalId, messageId, "foobar", now, now, httpStatus, Some(wireMockBaseUrlAsString)) + globalId, messageId, "foobar", "some url", now, now, httpStatus, Some(wireMockBaseUrlAsString)) val expectedStatus: Int = OK primeNotificationsEndpoint(expectedStatus) @@ -96,7 +96,7 @@ class NotificationCallbackConnectorISpec extends AnyWordSpec with Matchers with "handle failed requests to the notification URL" in new Setup { val message: OutboundSoapMessage = RetryingOutboundSoapMessage( - globalId, messageId, "foobar", now, now, httpStatus, Some(wireMockBaseUrlAsString)) + globalId, messageId, "foobar", "some url", now, now, httpStatus, Some(wireMockBaseUrlAsString)) val expectedStatus: Int = INTERNAL_SERVER_ERROR primeNotificationsEndpoint(expectedStatus) @@ -107,7 +107,7 @@ class NotificationCallbackConnectorISpec extends AnyWordSpec with Matchers with "recover from exceptions" in new Setup { val message: OutboundSoapMessage = RetryingOutboundSoapMessage( - globalId, messageId, "foobar", now, now, httpStatus, Some("https://invalidUrl")) + globalId, messageId, "foobar", "some url", now, now, httpStatus, Some("https://invalidUrl")) val result: Option[Int] = await(underTest.sendNotification(message)) diff --git a/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnectorISpec.scala b/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnectorISpec.scala index e86e741..174ac06 100644 --- a/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnectorISpec.scala +++ b/it/uk/gov/hmrc/apiplatformoutboundsoap/connectors/OutboundConnectorISpec.scala @@ -23,6 +23,7 @@ import play.api.Application import play.api.http.HeaderNames.CONTENT_TYPE import play.api.inject.guice.GuiceApplicationBuilder import play.api.test.Helpers._ +import uk.gov.hmrc.apiplatformoutboundsoap.models.SoapRequest import uk.gov.hmrc.apiplatformoutboundsoap.support.{Ccn2Service, WireMockSupport} class OutboundConnectorISpec extends AnyWordSpec with Matchers with GuiceOneAppPerSuite with WireMockSupport with Ccn2Service { @@ -31,9 +32,8 @@ class OutboundConnectorISpec extends AnyWordSpec with Matchers with GuiceOneAppP protected def appBuilder: GuiceApplicationBuilder = new GuiceApplicationBuilder() .configure( - "metrics.enabled" -> false, - "auditing.enabled" -> false, - "ccn2Url" -> wireMockBaseUrlAsString + "metrics.enabled" -> false, + "auditing.enabled" -> false ) trait Setup { @@ -41,11 +41,11 @@ class OutboundConnectorISpec extends AnyWordSpec with Matchers with GuiceOneAppP } "postMessage" should { - val message = "foobar" + val message = SoapRequest("foobar", wireMockBaseUrlAsString) "return successful statuses returned by the CCN2 service" in new Setup { val expectedStatus: Int = OK - primeCcn2Endpoint(message, expectedStatus) + primeCcn2Endpoint(message.soapEnvelope, expectedStatus) val result: Int = await(underTest.postMessage(message)) @@ -54,7 +54,7 @@ class OutboundConnectorISpec extends AnyWordSpec with Matchers with GuiceOneAppP "return error statuses returned by the CCN2 service" in new Setup { val expectedStatus: Int = INTERNAL_SERVER_ERROR - primeCcn2Endpoint(message, expectedStatus) + primeCcn2Endpoint(message.soapEnvelope, expectedStatus) val result: Int = await(underTest.postMessage(message)) @@ -62,15 +62,15 @@ class OutboundConnectorISpec extends AnyWordSpec with Matchers with GuiceOneAppP } "send the given message to the CCN2 service" in new Setup { - primeCcn2Endpoint(message, OK) + primeCcn2Endpoint(message.soapEnvelope, OK) await(underTest.postMessage(message)) - verifyRequestBody(message) + verifyRequestBody(message.soapEnvelope) } "send the right SOAP content type header" in new Setup { - primeCcn2Endpoint(message, OK) + primeCcn2Endpoint(message.soapEnvelope, OK) await(underTest.postMessage(message)) diff --git a/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala b/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala index 1d245ff..2a6b216 100644 --- a/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala +++ b/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala @@ -11,13 +11,12 @@ import org.scalatestplus.play.guice.GuiceOneAppPerSuite import play.api.Application import play.api.inject.guice.GuiceApplicationBuilder import play.api.libs.json.{JsObject, Json} -import play.api.test.Helpers.contentAsJson import reactivemongo.api.ReadPreference import reactivemongo.bson.BSONLong import reactivemongo.core.errors.DatabaseException +import reactivemongo.play.json.ImplicitBSONHandlers.JsObjectDocumentWriter import uk.gov.hmrc.apiplatformoutboundsoap.models.{FailedOutboundSoapMessage, RetryingOutboundSoapMessage, SendingStatus, SentOutboundSoapMessage} import uk.gov.hmrc.mongo.RepositoryPreparation -import reactivemongo.play.json.ImplicitBSONHandlers.JsObjectDocumentWriter import java.util.UUID.randomUUID @@ -38,16 +37,16 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with Matchers with Repo prepare(repo) } - val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", DateTime.now(UTC), DateTime.now(UTC), ccnHttpStatus) - val sentMessage = SentOutboundSoapMessage(randomUUID, Some("MessageId-A2"), "payload", DateTime.now(UTC), ccnHttpStatus) - val failedMessage = FailedOutboundSoapMessage(randomUUID, Some("MessageId-A3"), "payload", DateTime.now(UTC), ccnHttpStatus) + val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", "some url", DateTime.now(UTC), DateTime.now(UTC), ccnHttpStatus) + val sentMessage = SentOutboundSoapMessage(randomUUID, Some("MessageId-A2"), "payload", "some url", DateTime.now(UTC), ccnHttpStatus) + val failedMessage = FailedOutboundSoapMessage(randomUUID, Some("MessageId-A3"), "payload", "some url", DateTime.now(UTC), ccnHttpStatus) "persist" should { "insert a retrying message when it does not exist" in { await(repo.persist(retryingMessage)) val fetchedRecords = await(repo.findAll(ReadPreference.primaryPreferred)) - val Some(jsonRecord) = await(repo.collection.find(Json.obj()).one[JsObject]) + val Some(jsonRecord) = await(repo.collection.find(Json.obj(), Option.empty[JsObject]).one[JsObject]) (jsonRecord \ "status").as[String] shouldBe "RETRYING" @@ -59,7 +58,7 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with Matchers with Repo await(repo.persist(sentMessage)) val fetchedRecords = await(repo.findAll(ReadPreference.primaryPreferred)) - val Some(jsonRecord) = await(repo.collection.find(Json.obj()).one[JsObject]) + val Some(jsonRecord) = await(repo.collection.find(Json.obj(), Option.empty[JsObject]).one[JsObject]) (jsonRecord \ "status").as[String] shouldBe "SENT" fetchedRecords.size shouldBe 1 @@ -70,7 +69,7 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with Matchers with Repo await(repo.persist(failedMessage)) val fetchedRecords = await(repo.findAll(ReadPreference.primaryPreferred)) - val Some(jsonRecord) = await(repo.collection.find(Json.obj()).one[JsObject]) + val Some(jsonRecord) = await(repo.collection.find(Json.obj(), Option.empty[JsObject]).one[JsObject]) (jsonRecord \ "status").as[String] shouldBe "FAILED" fetchedRecords.size shouldBe 1 @@ -117,7 +116,8 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with Matchers with Repo "not retrieve retrying messages when they are not ready for retrying" in { val retryingMessageNotReadyForRetrying = RetryingOutboundSoapMessage( - randomUUID, Some("MessageId-A1"), "payload", DateTime.now(UTC), DateTime.now(UTC).plusHours(1), ccnHttpStatus) + randomUUID, Some("MessageId-A1"), "payload", "some url", DateTime.now(UTC), + DateTime.now(UTC).plusHours(1), ccnHttpStatus) await(repo.persist(retryingMessageNotReadyForRetrying)) await(repo.persist(sentMessage)) diff --git a/test/resources/definitions/CCN2.Service.Customs.Default.ICS.RiskAnalysisOrchestrationBAS_1.0.0_CCN2_1.0.0.wsdl b/test/resources/definitions/CCN2.Service.Customs.Default.ICS.RiskAnalysisOrchestrationBAS_1.0.0_CCN2_1.0.0.wsdl index 6a13764..c390322 100644 --- a/test/resources/definitions/CCN2.Service.Customs.Default.ICS.RiskAnalysisOrchestrationBAS_1.0.0_CCN2_1.0.0.wsdl +++ b/test/resources/definitions/CCN2.Service.Customs.Default.ICS.RiskAnalysisOrchestrationBAS_1.0.0_CCN2_1.0.0.wsdl @@ -43,7 +43,7 @@ - + http://{ccn2Host}:{ccn2Port}/CCN2.Service.Customs.EU.ICS.RiskAnalysisOrchestrationBAS diff --git a/test/uk/gov/hmrc/apiplatformoutboundsoap/controllers/OutboundControllerSpec.scala b/test/uk/gov/hmrc/apiplatformoutboundsoap/controllers/OutboundControllerSpec.scala index 382862b..6e9e37f 100644 --- a/test/uk/gov/hmrc/apiplatformoutboundsoap/controllers/OutboundControllerSpec.scala +++ b/test/uk/gov/hmrc/apiplatformoutboundsoap/controllers/OutboundControllerSpec.scala @@ -51,7 +51,7 @@ class OutboundControllerSpec extends AnyWordSpec with Matchers with GuiceOneAppP val fakeRequest = FakeRequest("POST", "/message") val message = Json.obj("wsdlUrl" -> "http://example.com/wsdl", "wsdlOperation" -> "theOp", "messageBody" -> "example") - val outboundSoapMessage = SentOutboundSoapMessage(UUID.randomUUID, Some("123"), "envelope", DateTime.now(UTC), 200) + val outboundSoapMessage = SentOutboundSoapMessage(UUID.randomUUID, Some("123"), "envelope", "some url", DateTime.now(UTC), 200) "return the response returned by the outbound service" in new Setup { when(outboundServiceMock.sendMessage(*)).thenReturn(successful(outboundSoapMessage)) diff --git a/test/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundServiceSpec.scala b/test/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundServiceSpec.scala index 1d7b45b..d4bc6e9 100644 --- a/test/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundServiceSpec.scala +++ b/test/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundServiceSpec.scala @@ -170,6 +170,7 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS messageCaptor.getValue.globalId shouldBe expectedGlobalId messageCaptor.getValue.createDateTime shouldBe expectedCreateDateTime messageCaptor.getValue.notificationUrl shouldBe messageRequest.notificationUrl + messageCaptor.getValue.destinationUrl shouldBe "http://example.com/CCN2.Service.Customs.EU.ICS.RiskAnalysisOrchestrationBAS" } } @@ -192,18 +193,20 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS messageCaptor.getValue.asInstanceOf[RetryingOutboundSoapMessage].retryDateTime shouldBe expectedCreateDateTime.plus(expectedInterval.toMillis) messageCaptor.getValue.notificationUrl shouldBe messageRequest.notificationUrl + messageCaptor.getValue.destinationUrl shouldBe "http://example.com/CCN2.Service.Customs.EU.ICS.RiskAnalysisOrchestrationBAS" } } "send the SOAP envelope returned from the security service to the connector" in new Setup { when(wsSecurityServiceMock.addUsernameToken(*)).thenReturn(expectedSoapEnvelope()) when(outboundMessageRepositoryMock.persist(*)(*)).thenReturn(successful(())) - val messageCaptor: ArgumentCaptor[String] = ArgumentCaptor.forClass(classOf[String]) + val messageCaptor: ArgumentCaptor[SoapRequest] = ArgumentCaptor.forClass(classOf[SoapRequest]) when(outboundConnectorMock.postMessage(messageCaptor.capture())).thenReturn(successful(expectedStatus)) await(underTest.sendMessage(messageRequest)) - getXmlDiff(messageCaptor.getValue, expectedSoapEnvelope()).build().hasDifferences shouldBe false + getXmlDiff(messageCaptor.getValue.soapEnvelope, expectedSoapEnvelope()).build().hasDifferences shouldBe false + messageCaptor.getValue.destinationUrl shouldBe "http://example.com/CCN2.Service.Customs.EU.ICS.RiskAnalysisOrchestrationBAS" } "send the expected SOAP envelope to the connector" in new Setup { @@ -276,7 +279,8 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS "retryMessages" should { "retry a message successfully" in new Setup { - val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", DateTime.now(UTC), DateTime.now(UTC), httpStatus) + val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", + "some url", DateTime.now(UTC), DateTime.now(UTC), httpStatus) when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.retryInterval).thenReturn(Duration("1s")) when(outboundConnectorMock.postMessage(*)).thenReturn(successful(OK)) @@ -290,13 +294,14 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS } "abort retrying messages if unexpected exception thrown" in new Setup { - val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), null, DateTime.now(UTC), + val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), null, "some url", DateTime.now(UTC), DateTime.now(UTC), httpStatus) val anotherRetryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), - "payload 2", DateTime.now(UTC), DateTime.now(UTC), httpStatus) + "payload 2", "another url", DateTime.now(UTC), DateTime.now(UTC), httpStatus) when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.retryDuration).thenReturn(Duration("1s")) - when(outboundConnectorMock.postMessage(anotherRetryingMessage.soapMessage)).thenReturn(successful(OK)) + when(outboundConnectorMock.postMessage(SoapRequest(anotherRetryingMessage.soapMessage, anotherRetryingMessage.destinationUrl))) + .thenReturn(successful(OK)) when(outboundMessageRepositoryMock.updateStatus(*, *)).thenReturn(successful(None)) when(outboundMessageRepositoryMock.retrieveMessagesForRetry). thenReturn(fromFutureSource(successful(fromIterator(() => Seq(retryingMessage, anotherRetryingMessage).toIterator)))) @@ -308,7 +313,7 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS "retry a message and persist with retrying status when SOAP request returned error status" in new Setup { val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", - DateTime.now(UTC), DateTime.now(UTC), httpStatus) + "some url", DateTime.now(UTC), DateTime.now(UTC), httpStatus) when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.retryDuration).thenReturn(Duration("5s")) when(appConfigMock.retryInterval).thenReturn(Duration("5s")) @@ -326,7 +331,7 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS "set a message's status to FAILED when its retryDuration has expired" in new Setup { val retryDuration: Duration = Duration("30s") val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", - DateTime.now(UTC).minus(retryDuration.plus(Duration("1s")).toMillis), DateTime.now(UTC), httpStatus) + "some url", DateTime.now(UTC).minus(retryDuration.plus(Duration("1s")).toMillis), DateTime.now(UTC), httpStatus) when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.retryInterval).thenReturn(Duration("5s")) when(appConfigMock.retryDuration).thenReturn(retryDuration) @@ -342,7 +347,8 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS } "notify the caller on the notification URL supplied that the retrying message is now SENT" in new Setup { - val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", DateTime.now(UTC), DateTime.now(UTC), httpStatus) + val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", + "some url", DateTime.now(UTC), DateTime.now(UTC), httpStatus) val sentMessageForNotification: SentOutboundSoapMessage = retryingMessage.toSent when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.parallelism).thenReturn(2) @@ -360,7 +366,7 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS "notify the caller on the notification URL supplied that the retrying message is now FAILED" in new Setup { val retryDuration: Duration = Duration("30s") val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", - DateTime.now(UTC).minus(retryDuration.plus(Duration("1s")).toMillis), DateTime.now(UTC), httpStatus) + "some url", DateTime.now(UTC).minus(retryDuration.plus(Duration("1s")).toMillis), DateTime.now(UTC), httpStatus) val failedMessageForNotification: FailedOutboundSoapMessage = retryingMessage.toFailed when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.retryInterval).thenReturn(Duration("5s")) @@ -378,7 +384,7 @@ class OutboundServiceSpec extends AnyWordSpec with Matchers with GuiceOneAppPerS "update the status of a successfully sent message to SENT even if calling the notification URL fails" in new Setup{ val retryDuration: Duration = Duration("30s") val retryingMessage = RetryingOutboundSoapMessage(randomUUID, Some("MessageId-A1"), "payload", - DateTime.now(UTC).minus(retryDuration.plus(Duration("1s")).toMillis), DateTime.now(UTC), httpStatus) + "some url", DateTime.now(UTC).minus(retryDuration.plus(Duration("1s")).toMillis), DateTime.now(UTC), httpStatus) val failedMessageForNotification: FailedOutboundSoapMessage = retryingMessage.toFailed when(appConfigMock.parallelism).thenReturn(2) when(appConfigMock.retryInterval).thenReturn(Duration("5s"))