From a9c3a1231470146a84415068543e56a4765f4412 Mon Sep 17 00:00:00 2001 From: petekirby-ee Date: Wed, 24 Jan 2024 14:16:05 +0000 Subject: [PATCH] APIS-6702 Mongo lib upgrades 0.74.0 to 1.7.0 (#77) * APIS-6702 Mongo lib upgrades 0.74.0 to 1.7.0 * APIS-6702 Remove enumerations * APIS-6702 Remove enumerations * APIS-6702 Add tests * APIS-6702 PR bot actions --- .../models/JsonFormats.scala | 10 +- .../models/OutboundSoapMessage.scala | 48 +++--- .../models/SealedTraitJsonFormatting.scala | 33 ++++ .../repositories/MongoFormatter.scala | 32 ++-- .../OutboundMessageRepository.scala | 14 +- .../apiplatformoutboundsoap/responses.scala | 38 +++-- .../services/OutboundService.scala | 2 +- .../OutboundMessageRepositoryISpec.scala | 129 ++++++++-------- project/AppDependencies.scala | 3 +- project/metals.sbt | 2 +- project/plugins.sbt | 4 +- .../repositories/MongoFormatterSpec.scala | 142 +++++++++++++++++- .../scheduled/SoapMessageRetryJobSpec.scala | 5 +- 13 files changed, 327 insertions(+), 135 deletions(-) create mode 100644 app/uk/gov/hmrc/apiplatformoutboundsoap/models/SealedTraitJsonFormatting.scala diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/JsonFormats.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/JsonFormats.scala index 6da3e46..0bbfe9c 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/JsonFormats.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/JsonFormats.scala @@ -69,23 +69,23 @@ object JsonFormats { implicit val outboundSoapMessageWrites: OWrites[OutboundSoapMessage] = { case r @ RetryingOutboundSoapMessage(_, _, _, _, _, _, _, _, _, _, _, _) => retryingSoapMessageFormatter.writes(r) ++ Json.obj( - "status" -> SendingStatus.RETRYING.entryName + "status" -> SendingStatus.RETRYING.toString() ) case f @ FailedOutboundSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => failedSoapMessageFormatter.writes(f) ++ Json.obj( - "status" -> SendingStatus.FAILED.entryName + "status" -> SendingStatus.FAILED.toString() ) case s @ SentOutboundSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => sentSoapMessageFormatter.writes(s) ++ Json.obj( - "status" -> SendingStatus.SENT.entryName + "status" -> SendingStatus.SENT.toString() ) case cod @ CodSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => codSoapMessageFormatter.writes(cod) ++ Json.obj( - "status" -> DeliveryStatus.COD.entryName + "status" -> DeliveryStatus.COD.toString() ) case coe @ CoeSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => coeSoapMessageFormatter.writes(coe) ++ Json.obj( - "status" -> DeliveryStatus.COE.entryName + "status" -> DeliveryStatus.COE.toString() ) } implicit val outboundSoapMessageFormatter: OFormat[OutboundSoapMessage] = OFormat(Json.reads[OutboundSoapMessage], outboundSoapMessageWrites) diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala index d6ed8e1..c39a001 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/OutboundSoapMessage.scala @@ -18,10 +18,9 @@ package uk.gov.hmrc.apiplatformoutboundsoap.models import java.time.Instant import java.util.UUID -import scala.collection.immutable import scala.reflect.classTag -import enumeratum.{Enum, EnumEntry, PlayJsonEnum} +import play.api.libs.json.Format sealed trait OutboundSoapMessage { val globalId: UUID @@ -143,38 +142,49 @@ case class RetryingOutboundSoapMessage( def toSent = SentOutboundSoapMessage(globalId, messageId, soapMessage, destinationUrl, createDateTime, ccnHttpStatus, notificationUrl, codMessage, coeMessage, Some(Instant.now)) } -sealed abstract class StatusType extends EnumEntry +sealed abstract trait StatusType -object StatusType extends Enum[StatusType] with PlayJsonEnum[StatusType] { - val values: immutable.IndexedSeq[StatusType] = findValues +object StatusType { + val values: Set[StatusType] = DeliveryStatus.values ++ SendingStatus.values + + def apply(text: String): Option[StatusType] = StatusType.values.find(_.toString() == text.toUpperCase) + + implicit val format: Format[StatusType] = SealedTraitJsonFormatting.createFormatFor[StatusType]("Status Type", apply) } -sealed abstract class DeliveryStatus(override val entryName: String) extends StatusType +sealed trait DeliveryStatus extends StatusType + +object DeliveryStatus { + case object COE extends DeliveryStatus + case object COD extends DeliveryStatus + val values: Set[DeliveryStatus] = Set(COE, COD) -object DeliveryStatus extends Enum[DeliveryStatus] with PlayJsonEnum[DeliveryStatus] { + def apply(text: String): Option[DeliveryStatus] = DeliveryStatus.values.find(_.toString() == text.toUpperCase) - def fromAction(action: String): DeliveryStatus = { + def unsafeApply(text: String): DeliveryStatus = apply(text).getOrElse(throw new RuntimeException(s"$text is not a valid Delivery Status")) + + def fromAction(action: String): DeliveryStatus = { action match { case "CCN2.Service.Platform.AcknowledgementService/CoE" => DeliveryStatus.COE case "CCN2.Service.Platform.AcknowledgementService/CoD" => DeliveryStatus.COD - case _ => throw new IllegalArgumentException(s"${action} is not a valid DeliveryStatus") + case _ => throw new IllegalArgumentException(s"${action} is not a valid Delivery Status") } } - val values: immutable.IndexedSeq[DeliveryStatus] = findValues - - case object COE extends DeliveryStatus("COE") - case object COD extends DeliveryStatus("COD") + implicit val format: Format[DeliveryStatus] = SealedTraitJsonFormatting.createFormatFor[DeliveryStatus]("Delivery Status", apply) } -sealed abstract class SendingStatus(override val entryName: String) extends StatusType +sealed trait SendingStatus extends StatusType -object SendingStatus extends Enum[SendingStatus] with PlayJsonEnum[SendingStatus] { - val values: immutable.IndexedSeq[SendingStatus] = findValues +object SendingStatus { + case object SENT extends SendingStatus + case object FAILED extends SendingStatus + case object RETRYING extends SendingStatus + val values: Set[SendingStatus] = Set(SENT, FAILED, RETRYING) - case object SENT extends SendingStatus("SENT") + def apply(text: String): Option[SendingStatus] = SendingStatus.values.find(_.toString() == text.toUpperCase) - case object FAILED extends SendingStatus("FAILED") + def unsafeApply(text: String): SendingStatus = apply(text).getOrElse(throw new RuntimeException(s"$text is not a valid Sending Status")) - case object RETRYING extends SendingStatus("RETRYING") + implicit val format: Format[SendingStatus] = SealedTraitJsonFormatting.createFormatFor[SendingStatus]("Sending Status", apply) } diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/models/SealedTraitJsonFormatting.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/SealedTraitJsonFormatting.scala new file mode 100644 index 0000000..e4cc227 --- /dev/null +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/models/SealedTraitJsonFormatting.scala @@ -0,0 +1,33 @@ +/* + * Copyright 2024 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 + +object SealedTraitJsonFormatting { + import play.api.libs.json._ + + def createFormatFor[T](name: String, read: String => Option[T], write: T => String = (t: T) => t.toString) = new Format[T] { + + def reads(json: JsValue): JsResult[T] = json match { + case JsString(text) => read(text).fold[JsResult[T]] { JsError(s"$text is not a valid $name") }(JsSuccess(_)) + case e => JsError(s"Cannot parse $name from '$e'") + } + + def writes(foo: T): JsValue = { + JsString(write(foo)) + } + } +} diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatter.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatter.scala index 4535d27..40e5438 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatter.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatter.scala @@ -27,7 +27,7 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici implicit val cfg: Aux[Json.MacroOptions] = JsonConfiguration( discriminator = "status", typeNaming = JsonNaming { fullName => - OutboundSoapMessage.typeToStatus(fullName).entryName + OutboundSoapMessage.typeToStatus(fullName).toString() } ) implicit val privateHeaderReads: Reads[PrivateHeader] = Json.reads[PrivateHeader] @@ -40,7 +40,7 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici Json.reads[RetryingOutboundSoapMessage] implicit val retryingMessageWrites: OWrites[RetryingOutboundSoapMessage] = - Json.writes[RetryingOutboundSoapMessage].transform(_ ++ Json.obj("status" -> SendingStatus.RETRYING.entryName)) + Json.writes[RetryingOutboundSoapMessage].transform(_ ++ Json.obj("status" -> SendingStatus.RETRYING.toString())) implicit val retryingSoapMessageFormatter: OFormat[RetryingOutboundSoapMessage] = OFormat(retryingMessageReads, retryingMessageWrites) @@ -49,7 +49,7 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici Json.reads[SentOutboundSoapMessage] implicit val sentMessageWrites: OWrites[SentOutboundSoapMessage] = - Json.writes[SentOutboundSoapMessage].transform(_ ++ Json.obj("status" -> SendingStatus.SENT.entryName)) + Json.writes[SentOutboundSoapMessage].transform(_ ++ Json.obj("status" -> SendingStatus.SENT.toString())) implicit val sentSoapMessageFormatter: OFormat[SentOutboundSoapMessage] = OFormat(sentMessageReads, sentMessageWrites) @@ -58,7 +58,7 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici Json.reads[FailedOutboundSoapMessage] implicit val failedMessageWrites: OWrites[FailedOutboundSoapMessage] = - Json.writes[FailedOutboundSoapMessage].transform(_ ++ Json.obj("status" -> SendingStatus.FAILED.entryName)) + Json.writes[FailedOutboundSoapMessage].transform(_ ++ Json.obj("status" -> SendingStatus.FAILED.toString())) implicit val failedSoapMessageFormatter: OFormat[FailedOutboundSoapMessage] = OFormat(failedMessageReads, failedMessageWrites) @@ -67,7 +67,7 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici Json.reads[CodSoapMessage] implicit val codMessageWrites: OWrites[CodSoapMessage] = - Json.writes[CodSoapMessage].transform(_ ++ Json.obj("status" -> DeliveryStatus.COD.entryName)) + Json.writes[CodSoapMessage].transform(_ ++ Json.obj("status" -> DeliveryStatus.COD.toString())) implicit val codSoapMessageFormatter: OFormat[CodSoapMessage] = OFormat(codMessageReads, codMessageWrites) @@ -76,22 +76,22 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici Json.reads[CoeSoapMessage] implicit val coeMessageWrites: OWrites[CoeSoapMessage] = - Json.writes[CoeSoapMessage].transform(_ ++ Json.obj("status" -> DeliveryStatus.COE.entryName)) + Json.writes[CoeSoapMessage].transform(_ ++ Json.obj("status" -> DeliveryStatus.COE.toString())) implicit val coeSoapMessageFormatter: OFormat[CoeSoapMessage] = OFormat(coeMessageReads, coeMessageWrites) implicit val outboundSoapMessageReads: Reads[OutboundSoapMessage] = (JsPath \ "status").read[String].flatMap { - case SendingStatus.RETRYING.entryName => + case "RETRYING" => retryingSoapMessageFormatter.widen[OutboundSoapMessage] - case SendingStatus.SENT.entryName => + case "SENT" => sentSoapMessageFormatter.widen[OutboundSoapMessage] - case SendingStatus.FAILED.entryName => + case "FAILED" => failedSoapMessageFormatter.widen[OutboundSoapMessage] - case DeliveryStatus.COD.entryName => + case "COD" => codSoapMessageFormatter.widen[OutboundSoapMessage] - case DeliveryStatus.COE.entryName => + case "COE" => coeSoapMessageFormatter.widen[OutboundSoapMessage] } @@ -100,23 +100,23 @@ private[repositories] object MongoFormatter extends MongoJavatimeFormats.Implici override def writes(soapMessage: OutboundSoapMessage): JsObject = soapMessage match { case r @ RetryingOutboundSoapMessage(_, _, _, _, _, _, _, _, _, _, _, _) => retryingSoapMessageFormatter.writes(r) ++ Json.obj( - "status" -> SendingStatus.RETRYING.entryName + "status" -> SendingStatus.RETRYING.toString() ) case f @ FailedOutboundSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => failedSoapMessageFormatter.writes(f) ++ Json.obj( - "status" -> SendingStatus.FAILED.entryName + "status" -> SendingStatus.FAILED.toString() ) case s @ SentOutboundSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => sentSoapMessageFormatter.writes(s) ++ Json.obj( - "status" -> SendingStatus.SENT.entryName + "status" -> SendingStatus.SENT.toString() ) case cod @ CodSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => codSoapMessageFormatter.writes(cod) ++ Json.obj( - "status" -> DeliveryStatus.COD.entryName + "status" -> DeliveryStatus.COD.toString() ) case coe @ CoeSoapMessage(_, _, _, _, _, _, _, _, _, _, _) => coeSoapMessageFormatter.writes(coe) ++ Json.obj( - "status" -> DeliveryStatus.COE.entryName + "status" -> DeliveryStatus.COE.toString() ) } } diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala index c1bdd78..8ee29e5 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepository.scala @@ -75,9 +75,9 @@ class OutboundMessageRepository @Inject() (mongoComponent: MongoComponent, appCo Codecs.playFormatCodec(MongoFormatter.sentSoapMessageFormatter), Codecs.playFormatCodec(MongoFormatter.codSoapMessageFormatter), Codecs.playFormatCodec(MongoFormatter.coeSoapMessageFormatter), - Codecs.playFormatCodec(StatusType.jsonFormat), - Codecs.playFormatCodec(DeliveryStatus.jsonFormat), - Codecs.playFormatCodec(SendingStatus.jsonFormat) + Codecs.playFormatCodec(StatusType.format), + Codecs.playFormatCodec(DeliveryStatus.format), + Codecs.playFormatCodec(SendingStatus.format) ), MongoClient.DEFAULT_CODEC_REGISTRY ) @@ -89,7 +89,7 @@ class OutboundMessageRepository @Inject() (mongoComponent: MongoComponent, appCo def retrieveMessagesForRetry: Source[RetryingOutboundSoapMessage, NotUsed] = { MongoSource(collection.withReadPreference(primaryPreferred()) - .find(filter = and(equal("status", SendingStatus.RETRYING.entryName), and(lte("retryDateTime", Codecs.toBson(Instant.now))))) + .find(filter = and(equal("status", SendingStatus.RETRYING.toString()), and(lte("retryDateTime", Codecs.toBson(Instant.now))))) .sort(ascending("retryDateTime")) .map(_.asInstanceOf[RetryingOutboundSoapMessage])) } @@ -107,7 +107,7 @@ class OutboundMessageRepository @Inject() (mongoComponent: MongoComponent, appCo collection.withReadPreference(primaryPreferred()) .findOneAndUpdate( filter = equal("globalId", Codecs.toBson(globalId)), - update = set("status", Codecs.toBson(newStatus.entryName)), + update = set("status", Codecs.toBson(newStatus.toString())), options = FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.AFTER) ).toFutureOption() } @@ -118,7 +118,7 @@ class OutboundMessageRepository @Inject() (mongoComponent: MongoComponent, appCo filter = equal("globalId", Codecs.toBson(globalId)), update = combine( set("sentDateTime", Codecs.toBson(sentInstant)), - set("status", Codecs.toBson(SendingStatus.SENT.entryName)) + set("status", Codecs.toBson(SendingStatus.SENT.toString())) ), options = FindOneAndUpdateOptions().upsert(true).returnDocument(ReturnDocument.AFTER) ).toFutureOption() @@ -132,7 +132,7 @@ class OutboundMessageRepository @Inject() (mongoComponent: MongoComponent, appCo for { _ <- collection.bulkWrite( - List(UpdateManyModel(Document("messageId" -> messageId), combine(set("status", Codecs.toBson(newStatus.entryName)), set(field, confirmationMsg)))), + List(UpdateManyModel(Document("messageId" -> messageId), combine(set("status", Codecs.toBson(newStatus.toString())), set(field, confirmationMsg)))), BulkWriteOptions().ordered(false) ).toFuture() findUpdated <- findById(messageId) diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/responses.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/responses.scala index e66b056..849c439 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/responses.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/responses.scala @@ -19,24 +19,38 @@ package uk.gov.hmrc.apiplatformoutboundsoap import play.api.libs.json.Json.JsValueWrapper import play.api.libs.json.{JsObject, Json} -object ErrorCode extends Enumeration { - type ErrorCode = Value - val NOT_FOUND = Value("NOT_FOUND") - val BAD_REQUEST = Value("BAD_REQUEST") - val INTERNAL_SERVER_ERROR = Value("INTERNAL_SERVER_ERROR") +sealed trait ErrorCode + +object ErrorCode { + case object NOT_FOUND extends ErrorCode + case object BAD_REQUEST extends ErrorCode + case object INTERNAL_SERVER_ERROR extends ErrorCode + + val values: Set[ErrorCode] = Set(NOT_FOUND, BAD_REQUEST, INTERNAL_SERVER_ERROR) + + def apply(text: String): Option[ErrorCode] = ErrorCode.values.find(_.toString() == text.toUpperCase) + + def unsafeApply(text: String): ErrorCode = apply(text).getOrElse(throw new RuntimeException(s"$text is not a valid Error Code")) } -object CcnRequestResult extends Enumeration { - type CcnRequestResult = Value - val UNEXPECTED_SUCCESS = Value("UNEXPECTED_SUCCESS") - val SUCCESS = Value("SUCCESS") - val FAIL_ERROR = Value("FAIL_ERROR") - val RETRYABLE_ERROR = Value("RETRYABLE_ERROR") +sealed trait CcnRequestResult + +object CcnRequestResult { + case object UNEXPECTED_SUCCESS extends CcnRequestResult + case object SUCCESS extends CcnRequestResult + case object FAIL_ERROR extends CcnRequestResult + case object RETRYABLE_ERROR extends CcnRequestResult + + val values: Set[CcnRequestResult] = Set(UNEXPECTED_SUCCESS, SUCCESS, FAIL_ERROR, RETRYABLE_ERROR) + + def apply(text: String): Option[CcnRequestResult] = CcnRequestResult.values.find(_.toString() == text.toUpperCase) + + def unsafeApply(text: String): CcnRequestResult = apply(text).getOrElse(throw new RuntimeException(s"$text is not a valid CCN Request Result")) } object JsErrorResponse { - def apply(errorCode: ErrorCode.Value, message: JsValueWrapper): JsObject = + def apply(errorCode: ErrorCode, message: JsValueWrapper): JsObject = Json.obj( "code" -> errorCode.toString, "message" -> message diff --git a/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala b/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala index 652a2fe..861b463 100644 --- a/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala +++ b/app/uk/gov/hmrc/apiplatformoutboundsoap/services/OutboundService.scala @@ -281,7 +281,7 @@ class OutboundService @Inject() ( envelope.getBody.addChild(payload) } - private def mapHttpStatusCode(httpStatusCode: Int): CcnRequestResult.Value = { + private def mapHttpStatusCode(httpStatusCode: Int): CcnRequestResult = { if (isSuccessful(httpStatusCode)) { httpStatusCode match { case ACCEPTED => SUCCESS diff --git a/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala b/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala index 6dd6927..ff89048 100644 --- a/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala +++ b/it/uk/gov/hmrc/apiplatformoutboundsoap/repositories/OutboundMessageRepositoryISpec.scala @@ -35,14 +35,13 @@ import org.scalatestplus.play.guice.GuiceOneAppPerSuite import play.api.Application import play.api.inject.guice.GuiceApplicationBuilder import play.api.test.Helpers.{await, defaultAwaitTimeout} -import uk.gov.hmrc.mongo.play.json.PlayMongoRepository import uk.gov.hmrc.mongo.test.PlayMongoRepositorySupport import uk.gov.hmrc.apiplatformoutboundsoap.models._ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositorySupport[OutboundSoapMessage] with Matchers with BeforeAndAfterEach with GuiceOneAppPerSuite with IntegrationPatience { - val serviceRepo = repository.asInstanceOf[OutboundMessageRepository] + val repository = app.injector.instanceOf[OutboundMessageRepository] override implicit lazy val app: Application = appBuilder.build() val ccnHttpStatus: Int = 200 @@ -76,14 +75,12 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "mongodb.uri" -> s"mongodb://127.0.0.1:27017/test-${this.getClass.getSimpleName}" ) - override protected def repository: PlayMongoRepository[OutboundSoapMessage] = app.injector.instanceOf[OutboundMessageRepository] - "persist" should { "insert a retrying message when it does not exist" in { - await(serviceRepo.persist(retryingMessage)) + await(repository.persist(retryingMessage)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head shouldBe retryingMessage @@ -91,9 +88,9 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor } "insert a sent message when it does not exist" in { - await(serviceRepo.persist(sentMessage)) + await(repository.persist(sentMessage)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head shouldBe sentMessage @@ -101,27 +98,27 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor } "insert a failed message when it does not exist" in { - await(serviceRepo.persist(failedMessage)) + await(repository.persist(failedMessage)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head shouldBe failedMessage fetchedRecords.head.status shouldBe SendingStatus.FAILED } "insert a confirmation of exception message when it does not exist" in { - await(serviceRepo.persist(coeMessage)) + await(repository.persist(coeMessage)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head shouldBe coeMessage fetchedRecords.head.status shouldBe DeliveryStatus.COE } "insert a confirmation of delivery message when it does not exist" in { - await(serviceRepo.persist(codMessage)) + await(repository.persist(codMessage)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head shouldBe codMessage @@ -129,35 +126,35 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor } "message is persisted with TTL" in { - await(serviceRepo.persist(sentMessage)) + await(repository.persist(sentMessage)) - val Some(ttlIndex) = await(serviceRepo.collection.listIndexes().toFuture()).find(i => i.get("name").get.asString().getValue == "ttlIndex") + val Some(ttlIndex) = await(repository.collection.listIndexes().toFuture()).find(i => i.get("name").get.asString().getValue == "ttlIndex") ttlIndex.get("unique") shouldBe None ttlIndex.get("background").get shouldBe BsonBoolean(true) ttlIndex.get("expireAfterSeconds").get.asNumber().intValue shouldBe 60 * 60 * 24 * 30 } "message is persisted with unique ID" in { - await(serviceRepo.persist(sentMessage)) + await(repository.persist(sentMessage)) - val Some(globalIdIndex) = await(serviceRepo.collection.listIndexes().toFuture()).find(i => i.get("name").get.asString().getValue == "globalIdIndex") + val Some(globalIdIndex) = await(repository.collection.listIndexes().toFuture()).find(i => i.get("name").get.asString().getValue == "globalIdIndex") globalIdIndex.get("unique") shouldBe Some(BsonBoolean(true)) globalIdIndex.get("background").get shouldBe BsonBoolean(true) } "create index on message ID" in { - await(serviceRepo.persist(sentMessage)) + await(repository.persist(sentMessage)) - val Some(globalIdIndex) = await(serviceRepo.collection.listIndexes().toFuture()).find(i => i.get("name").get.asString().getValue == "messageIdIndex") + val Some(globalIdIndex) = await(repository.collection.listIndexes().toFuture()).find(i => i.get("name").get.asString().getValue == "messageIdIndex") globalIdIndex.get("unique") shouldBe None globalIdIndex.get("background").get shouldBe BsonBoolean(true) } "fail when a message with the same ID already exists" in { - await(serviceRepo.persist(retryingMessage)) + await(repository.persist(retryingMessage)) val exception: MongoWriteException = intercept[MongoWriteException] { - await(serviceRepo.persist(retryingMessage)) + await(repository.persist(retryingMessage)) } exception.getMessage should include("E11000 duplicate key error collection") @@ -166,10 +163,10 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "retrieveMessagesForRetry" should { "retrieve retrying messages and ignore sent messages" in { - await(serviceRepo.persist(retryingMessage)) - await(serviceRepo.persist(sentMessage)) + await(repository.persist(retryingMessage)) + await(repository.persist(sentMessage)) - val fetchedRecords = await(serviceRepo.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) + val fetchedRecords = await(repository.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) fetchedRecords.size shouldBe 1 fetchedRecords.head shouldBe retryingMessage } @@ -185,21 +182,21 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor ccnHttpStatus ) - await(serviceRepo.persist(retryingMessageNotReadyForRetrying)) - await(serviceRepo.persist(sentMessage)) + await(repository.persist(retryingMessageNotReadyForRetrying)) + await(repository.persist(sentMessage)) - val fetchedRecords = await(serviceRepo.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) + val fetchedRecords = await(repository.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) fetchedRecords.size shouldBe 0 } "retrieve retrying messages with retryDate in ascending order" in { val retryingMessageOldRetryDatetime = retryingMessage.copy(globalId = randomUUID, retryDateTime = retryingMessage.retryDateTime.minus(Duration.ofHours(1))) val retryingMessageEvenOlderRetryDatetime = retryingMessage.copy(globalId = randomUUID, retryDateTime = retryingMessage.retryDateTime.minus(Duration.ofHours(2))) - await(serviceRepo.persist(retryingMessageEvenOlderRetryDatetime)) - await(serviceRepo.persist(retryingMessage)) - await(serviceRepo.persist(retryingMessageOldRetryDatetime)) + await(repository.persist(retryingMessageEvenOlderRetryDatetime)) + await(repository.persist(retryingMessage)) + await(repository.persist(retryingMessageOldRetryDatetime)) - val fetchedRecords = await(serviceRepo.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) + val fetchedRecords = await(repository.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) fetchedRecords.size shouldBe 3 fetchedRecords.head shouldBe retryingMessageEvenOlderRetryDatetime fetchedRecords(1) shouldBe retryingMessageOldRetryDatetime @@ -209,20 +206,20 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "updateNextRetryTime" should { "update the retryDateTime on a record given its globalId" in { - await(serviceRepo.persist(retryingMessage)) + await(repository.persist(retryingMessage)) val newRetryDateTime = retryingMessage.retryDateTime.minus(Duration.ofHours(2)) - await(serviceRepo.updateNextRetryTime(retryingMessage.globalId, newRetryDateTime)) + await(repository.updateNextRetryTime(retryingMessage.globalId, newRetryDateTime)) - val fetchedRecords = await(serviceRepo.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) + val fetchedRecords = await(repository.retrieveMessagesForRetry.runWith(Sink.seq[RetryingOutboundSoapMessage])) fetchedRecords.size shouldBe 1 fetchedRecords.head.retryDateTime shouldBe newRetryDateTime } "updated message is returned from the database after updating retryDateTime" in { - await(serviceRepo.persist(retryingMessage)) + await(repository.persist(retryingMessage)) val newRetryDateTime = retryingMessage.retryDateTime.minus(Duration.ofHours(2)) - val Some(updatedMessage) = await(serviceRepo.updateNextRetryTime(retryingMessage.globalId, newRetryDateTime)) + val Some(updatedMessage) = await(repository.updateNextRetryTime(retryingMessage.globalId, newRetryDateTime)) updatedMessage.retryDateTime shouldBe newRetryDateTime } @@ -230,10 +227,10 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "updateStatus" should { "update the message to have a status of FAILED" in { - await(serviceRepo.persist(retryingMessage)) - val Some(returnedSoapMessage) = await(serviceRepo.updateSendingStatus(retryingMessage.globalId, SendingStatus.FAILED)) + await(repository.persist(retryingMessage)) + val Some(returnedSoapMessage) = await(repository.updateSendingStatus(retryingMessage.globalId, SendingStatus.FAILED)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head.status shouldBe SendingStatus.FAILED fetchedRecords.head.isInstanceOf[FailedOutboundSoapMessage] shouldBe true @@ -242,10 +239,10 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor } "update the message to have a status of SENT" in { - await(serviceRepo.persist(retryingMessage)) - val Some(returnedSoapMessage) = await(serviceRepo.updateToSent(retryingMessage.globalId, instantNow)) + await(repository.persist(retryingMessage)) + val Some(returnedSoapMessage) = await(repository.updateToSent(retryingMessage.globalId, instantNow)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head.status shouldBe SendingStatus.SENT fetchedRecords.head.isInstanceOf[SentOutboundSoapMessage] shouldBe true @@ -259,20 +256,20 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "updateConfirmationStatus" should { val expectedConfirmationMessageBody = "foobar" "update a message when a CoE is received" in { - await(serviceRepo.persist(sentMessage)) - await(serviceRepo.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COE, expectedConfirmationMessageBody)) + await(repository.persist(sentMessage)) + await(repository.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COE, expectedConfirmationMessageBody)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head.status shouldBe DeliveryStatus.COE fetchedRecords.head.asInstanceOf[CoeSoapMessage].coeMessage shouldBe Some(expectedConfirmationMessageBody) } "update a message when a CoD is received" in { - await(serviceRepo.persist(sentMessage)) - await(serviceRepo.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COD, expectedConfirmationMessageBody)) + await(repository.persist(sentMessage)) + await(repository.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COD, expectedConfirmationMessageBody)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 1 fetchedRecords.head.status shouldBe DeliveryStatus.COD @@ -281,12 +278,12 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "update all records with the same messageId when a CoE is received" in { val secondSentMessage = sentMessage.copy(globalId = randomUUID()) - await(serviceRepo.persist(sentMessage)) - await(serviceRepo.persist(secondSentMessage)) + await(repository.persist(sentMessage)) + await(repository.persist(secondSentMessage)) - await(serviceRepo.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COE, expectedConfirmationMessageBody)) + await(repository.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COE, expectedConfirmationMessageBody)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 2 fetchedRecords.head.globalId shouldBe sentMessage.globalId @@ -315,12 +312,12 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor "update all records with the same messageId when a CoD is received" in { val secondSentMessage = sentMessage.copy(globalId = randomUUID()) - await(serviceRepo.persist(sentMessage)) - await(serviceRepo.persist(secondSentMessage)) + await(repository.persist(sentMessage)) + await(repository.persist(secondSentMessage)) - await(serviceRepo.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COD, expectedConfirmationMessageBody)) + await(repository.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COD, expectedConfirmationMessageBody)) - val fetchedRecords = await(serviceRepo.collection.withReadPreference(primaryPreferred()).find().toFuture()) + val fetchedRecords = await(repository.collection.withReadPreference(primaryPreferred()).find().toFuture()) fetchedRecords.size shouldBe 2 fetchedRecords.head.globalId shouldBe sentMessage.globalId @@ -346,34 +343,34 @@ class OutboundMessageRepositoryISpec extends AnyWordSpec with PlayMongoRepositor } "ensure that unknown messageId returns empty option" in { - val emptyMessage = await(serviceRepo.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COD, expectedConfirmationMessageBody)) + val emptyMessage = await(repository.updateConfirmationStatus(sentMessage.messageId, DeliveryStatus.COD, expectedConfirmationMessageBody)) emptyMessage shouldBe None } } "findById" should { "return message when messageId matches" in { - await(serviceRepo.persist(sentMessage)) - val Some(found): Option[OutboundSoapMessage] = await(serviceRepo.findById(sentMessage.messageId)) + await(repository.persist(sentMessage)) + val Some(found): Option[OutboundSoapMessage] = await(repository.findById(sentMessage.messageId)) found shouldBe sentMessage } "return message when globalId matches" in { - await(serviceRepo.persist(sentMessage)) - val Some(found): Option[OutboundSoapMessage] = await(serviceRepo.findById(sentMessage.globalId.toString)) + await(repository.persist(sentMessage)) + val Some(found): Option[OutboundSoapMessage] = await(repository.findById(sentMessage.globalId.toString)) found shouldBe sentMessage } "return nothing when ID does not exist" in { - val found: Option[OutboundSoapMessage] = await(serviceRepo.findById(sentMessage.messageId)) + val found: Option[OutboundSoapMessage] = await(repository.findById(sentMessage.messageId)) found shouldBe None } "return newest message for a given messageId" in { - await(serviceRepo.persist(sentMessage)) - await(serviceRepo.persist(sentMessage.copy(createDateTime = instantNow.minus(Duration.ofHours(1)), globalId = randomUUID()))) + await(repository.persist(sentMessage)) + await(repository.persist(sentMessage.copy(createDateTime = instantNow.minus(Duration.ofHours(1)), globalId = randomUUID()))) - val Some(found): Option[OutboundSoapMessage] = await(serviceRepo.findById(sentMessage.messageId)) + val Some(found): Option[OutboundSoapMessage] = await(repository.findById(sentMessage.messageId)) found shouldBe sentMessage } } diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala index 8a1e131..08f35bb 100644 --- a/project/AppDependencies.scala +++ b/project/AppDependencies.scala @@ -4,7 +4,7 @@ import sbt._ object AppDependencies { val bootstrapPlayVersion = "7.12.0" - val mongoVersion = "0.74.0" + val mongoVersion = "1.7.0" val compile = Seq( "uk.gov.hmrc" %% "bootstrap-backend-play-28" % bootstrapPlayVersion, @@ -13,7 +13,6 @@ object AppDependencies { "com.lightbend.akka" %% "akka-stream-alpakka-mongodb" % "3.0.4", "org.apache.axis2" % "axis2-kernel" % "1.8.0", "org.apache.wss4j" % "wss4j-ws-security-dom" % "2.4.1", - "com.beachape" %% "enumeratum-play-json" % "1.7.0", caffeine ) diff --git a/project/metals.sbt b/project/metals.sbt index cbb25c6..4c9df44 100644 --- a/project/metals.sbt +++ b/project/metals.sbt @@ -2,5 +2,5 @@ // This file enables sbt-bloop to create bloop config files. -addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.11") +addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.5.13") diff --git a/project/plugins.sbt b/project/plugins.sbt index 3f3ca5b..b8df339 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -6,9 +6,9 @@ resolvers ++= Seq( resolvers += Resolver.typesafeRepo("releases") -addSbtPlugin("uk.gov.hmrc" % "sbt-auto-build" % "3.18.0") +addSbtPlugin("uk.gov.hmrc" % "sbt-auto-build" % "3.20.0") addSbtPlugin("uk.gov.hmrc" % "sbt-distributables" % "2.4.0") -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.18") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.21") addSbtPlugin("org.scoverage" % "sbt-scoverage" % "2.0.9") addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") diff --git a/test/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatterSpec.scala b/test/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatterSpec.scala index 185abe7..4542d4a 100644 --- a/test/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatterSpec.scala +++ b/test/uk/gov/hmrc/apiplatformoutboundsoap/repositories/MongoFormatterSpec.scala @@ -17,20 +17,21 @@ package uk.gov.hmrc.apiplatformoutboundsoap.repositories import java.time.Instant +import java.time.temporal.ChronoUnit.MILLIS import java.util.UUID import org.mockito.{ArgumentMatchersSugar, MockitoSugar} import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec -import play.api.libs.json.{JsObject, JsString} +import play.api.libs.json.{JsObject, JsString, Json} import uk.gov.hmrc.mongo.play.json.formats.MongoJavatimeFormats import uk.gov.hmrc.apiplatformoutboundsoap.models._ class MongoFormatterSpec extends AnyWordSpec with Matchers with MockitoSugar with ArgumentMatchersSugar { - "format" should { + "format writes" should { val formatter = MongoFormatter.outboundSoapMessageWrites "correctly write a COD message" in { val msgJson: JsObject = @@ -86,4 +87,141 @@ class MongoFormatterSpec extends AnyWordSpec with Matchers with MockitoSugar wit msgJson.value.get("status") shouldBe Some(JsString("RETRYING")) } } + + "format reads" should { + val formatter = MongoFormatter.outboundSoapMessageReads + + "correctly read a COD message" in { + + val messageId = "ISALIVE-1703124554-V1" + val createDateTimeInstant = Instant.now.truncatedTo(MILLIS) + val createDateTimeJsValue = MongoJavatimeFormats.instantWrites.writes(createDateTimeInstant) + + val msgJson = Json.obj( + "globalId" -> "6fa2d156-5b35-4c0b-8f31-e3a7d4fe278a", + "messageId" -> messageId, + "soapMessage" -> "soap message", + "destinationUrl" -> "https://ccn.conf.hmrc.gov.uk:443/CCN2.Service.Customs.EU.ICS.ENSLifecycleManagementBASV2", + "createDateTime" -> createDateTimeJsValue, + "ccnHttpStatus" -> 202, + "status" -> "COD", + "codMessage" -> "cod message" + ) + + val msg = formatter.reads(msgJson) + + msg.isSuccess shouldBe true + msg.get.isInstanceOf[CodSoapMessage] shouldBe true + msg.get.messageId shouldBe messageId + msg.get.createDateTime shouldBe createDateTimeInstant + msg.get.codMessage shouldBe Some("cod message") + } + + "correctly read a SENT message" in { + + val messageId = "ISALIVE-1703124554-V1" + val createDateTimeInstant = Instant.now.truncatedTo(MILLIS) + val createDateTimeJsValue = MongoJavatimeFormats.instantWrites.writes(createDateTimeInstant) + + val msgJson = Json.obj( + "globalId" -> "6fa2d156-5b35-4c0b-8f31-e3a7d4fe278a", + "messageId" -> messageId, + "soapMessage" -> "soap message", + "destinationUrl" -> "https://ccn.conf.hmrc.gov.uk:443/CCN2.Service.Customs.EU.ICS.ENSLifecycleManagementBASV2", + "createDateTime" -> createDateTimeJsValue, + "ccnHttpStatus" -> 202, + "status" -> "SENT", + "codMessage" -> "cod message" + ) + + val msg = formatter.reads(msgJson) + + msg.isSuccess shouldBe true + msg.get.isInstanceOf[SentOutboundSoapMessage] shouldBe true + msg.get.messageId shouldBe messageId + msg.get.createDateTime shouldBe createDateTimeInstant + } + + "correctly read a FAILED message" in { + + val messageId = "ISALIVE-1703124554-V1" + val createDateTimeInstant = Instant.now.truncatedTo(MILLIS) + val createDateTimeJsValue = MongoJavatimeFormats.instantWrites.writes(createDateTimeInstant) + + val msgJson = Json.obj( + "globalId" -> "6fa2d156-5b35-4c0b-8f31-e3a7d4fe278a", + "messageId" -> messageId, + "soapMessage" -> "soap message", + "destinationUrl" -> "https://ccn.conf.hmrc.gov.uk:443/CCN2.Service.Customs.EU.ICS.ENSLifecycleManagementBASV2", + "createDateTime" -> createDateTimeJsValue, + "ccnHttpStatus" -> 202, + "status" -> "FAILED", + "codMessage" -> "cod message" + ) + + val msg = formatter.reads(msgJson) + + msg.isSuccess shouldBe true + msg.get.isInstanceOf[FailedOutboundSoapMessage] shouldBe true + msg.get.messageId shouldBe messageId + msg.get.createDateTime shouldBe createDateTimeInstant + } + + "correctly read a COE message" in { + + val messageId = "ISALIVE-1703124554-V1" + val createDateTimeInstant = Instant.now.truncatedTo(MILLIS) + val createDateTimeJsValue = MongoJavatimeFormats.instantWrites.writes(createDateTimeInstant) + + val msgJson = Json.obj( + "globalId" -> "6fa2d156-5b35-4c0b-8f31-e3a7d4fe278a", + "messageId" -> messageId, + "soapMessage" -> "soap message", + "destinationUrl" -> "https://ccn.conf.hmrc.gov.uk:443/CCN2.Service.Customs.EU.ICS.ENSLifecycleManagementBASV2", + "createDateTime" -> createDateTimeJsValue, + "ccnHttpStatus" -> 202, + "status" -> "COE", + "codMessage" -> "cod message", + "coeMessage" -> "coe message" + ) + + val msg = formatter.reads(msgJson) + + msg.isSuccess shouldBe true + msg.get.isInstanceOf[CoeSoapMessage] shouldBe true + msg.get.messageId shouldBe messageId + msg.get.createDateTime shouldBe createDateTimeInstant + msg.get.coeMessage shouldBe Some("coe message") + } + + "correctly read a RETRYING message" in { + + val messageId = "ISALIVE-1703124554-V1" + val createDateTimeInstant = Instant.now.truncatedTo(MILLIS) + val createDateTimeJsValue = MongoJavatimeFormats.instantWrites.writes(createDateTimeInstant) + val retryDateTimeInstant = Instant.now.truncatedTo(MILLIS) + val retryDateTimeJsValue = MongoJavatimeFormats.instantWrites.writes(retryDateTimeInstant) + + val msgJson = Json.obj( + "globalId" -> "6fa2d156-5b35-4c0b-8f31-e3a7d4fe278a", + "messageId" -> messageId, + "soapMessage" -> "soap message", + "destinationUrl" -> "https://ccn.conf.hmrc.gov.uk:443/CCN2.Service.Customs.EU.ICS.ENSLifecycleManagementBASV2", + "createDateTime" -> createDateTimeJsValue, + "retryDateTime" -> retryDateTimeJsValue, + "ccnHttpStatus" -> 202, + "status" -> "RETRYING", + "codMessage" -> "cod message", + "coeMessage" -> "coe message" + ) + + val msg = formatter.reads(msgJson) + + msg.isSuccess shouldBe true + msg.get.isInstanceOf[RetryingOutboundSoapMessage] shouldBe true + msg.get.messageId shouldBe messageId + msg.get.createDateTime shouldBe createDateTimeInstant + msg.get.coeMessage shouldBe Some("coe message") + } + } } diff --git a/test/uk/gov/hmrc/apiplatformoutboundsoap/scheduled/SoapMessageRetryJobSpec.scala b/test/uk/gov/hmrc/apiplatformoutboundsoap/scheduled/SoapMessageRetryJobSpec.scala index 842492e..8428c0b 100644 --- a/test/uk/gov/hmrc/apiplatformoutboundsoap/scheduled/SoapMessageRetryJobSpec.scala +++ b/test/uk/gov/hmrc/apiplatformoutboundsoap/scheduled/SoapMessageRetryJobSpec.scala @@ -16,6 +16,7 @@ package uk.gov.hmrc.apiplatformoutboundsoap.scheduled +import java.time.Instant import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future.{failed, successful} import scala.concurrent.duration.Duration @@ -26,7 +27,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.wordspec.AnyWordSpec import play.api.test.Helpers._ -import uk.gov.hmrc.mongo.lock.MongoLockRepository +import uk.gov.hmrc.mongo.lock.{Lock, MongoLockRepository} import uk.gov.hmrc.apiplatformoutboundsoap.config.AppConfig import uk.gov.hmrc.apiplatformoutboundsoap.services.OutboundService @@ -62,7 +63,7 @@ class SoapMessageRetryJobSpec extends AnyWordSpec with Matchers with MockitoSuga "execute" should { "return response for success" in new Setup { when(outboundServiceMock.retryMessages(*)).thenReturn(successful(Done)) - when(repositoryMock.takeLock(*, *, *)).thenReturn(successful(true)) + when(repositoryMock.takeLock(*, *, *)).thenReturn(successful(Some(Lock("", "", Instant.now, Instant.now)))) when(repositoryMock.releaseLock(*, *)).thenReturn(successful(())) val result: underTest.Result = await(underTest.execute)