From f93d284eb40e4068466563e1f48b45a4693eabb3 Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Wed, 12 Oct 2022 13:34:39 +0100 Subject: [PATCH 1/7] APID-520 - Porting hmrc mongo driver - Initial changes --- .../AcceptanceTestSpec.scala | 5 +- .../FieldDefinitionRepository.scala | 128 +++++++---- .../repository/MongoCrudHelper.scala | 135 ++++++------ .../repository/MongoDb.scala | 46 ++-- .../repository/MongoErrorHandler.scala | 86 +++++--- .../repository/MongoIndexCreator.scala | 64 +++--- .../SubscriptionFieldsRepository.scala | 201 ++++++++++++------ conf/application.conf | 2 +- project/AppDependencies.scala | 17 +- .../ApiFieldDefinitionsRepositorySpec.scala | 68 +++--- .../repository/MongoErrorHandlerSpec.scala | 200 +++++++++-------- .../SubscriptionFieldsRepositorySpec.scala | 71 ++++--- 12 files changed, 603 insertions(+), 420 deletions(-) diff --git a/acceptance/uk/gov/hmrc/apisubscriptionfields/AcceptanceTestSpec.scala b/acceptance/uk/gov/hmrc/apisubscriptionfields/AcceptanceTestSpec.scala index fee7de3..d19691b 100644 --- a/acceptance/uk/gov/hmrc/apisubscriptionfields/AcceptanceTestSpec.scala +++ b/acceptance/uk/gov/hmrc/apisubscriptionfields/AcceptanceTestSpec.scala @@ -17,7 +17,6 @@ package uk.gov.hmrc.apisubscriptionfields import java.util.UUID - import org.scalatest._ import org.scalatestplus.play.guice.GuiceOneServerPerSuite import play.api.Application @@ -27,7 +26,6 @@ import play.api.mvc._ import play.api.mvc.request.RequestTarget import play.api.test.FakeRequest import play.api.test.Helpers._ -import play.modules.reactivemongo.ReactiveMongoComponent import uk.gov.hmrc.apisubscriptionfields.model._ import scala.concurrent.ExecutionContext.Implicits.global @@ -35,6 +33,7 @@ import scala.concurrent.duration._ import scala.concurrent.{Await, Future} import cats.data.NonEmptyList import uk.gov.hmrc.apisubscriptionfields.controller.Helper +import uk.gov.hmrc.mongo.MongoComponent trait AcceptanceTestSpec extends FeatureSpec with GivenWhenThen @@ -97,7 +96,7 @@ trait AcceptanceTestSpec extends FeatureSpec } private def dropDatabase(): Unit = { - await( app.injector.instanceOf[ReactiveMongoComponent].mongoConnector.db().drop()) + await( app.injector.instanceOf[MongoComponent].database.drop().toFuture()) } def createRequest(method: String, path: String) = diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala index 60caf4a..1a427d7 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala @@ -16,18 +16,24 @@ package uk.gov.hmrc.apisubscriptionfields.repository -import javax.inject.{Inject, Singleton} +import akka.stream.Materializer import com.google.inject.ImplementedBy +import org.bson.codecs.configuration.CodecRegistries.{fromCodecs, fromRegistries} +import org.mongodb.scala.model.Filters.{and, equal} +import org.mongodb.scala.model.Indexes.ascending +import org.mongodb.scala.model.{Filters, IndexModel, IndexOptions} +import org.mongodb.scala.{MongoClient, MongoCollection} import play.api.libs.json._ -import reactivemongo.api.indexes.IndexType -import reactivemongo.bson.BSONObjectID -import reactivemongo.play.json.collection.JSONCollection -import uk.gov.hmrc.mongo.ReactiveRepository -import uk.gov.hmrc.mongo.json.ReactiveMongoFormats +import uk.gov.hmrc.apisubscriptionfields.model.JsonFormatters.ApiFieldDefinitionsJF +import uk.gov.hmrc.apisubscriptionfields.model.Types._ import uk.gov.hmrc.apisubscriptionfields.model._ -import Types._ +import uk.gov.hmrc.apisubscriptionfields.utils.ApplicationLogger +import uk.gov.hmrc.mongo.MongoComponent +import uk.gov.hmrc.mongo.play.json.{Codecs, CollectionFactory, PlayMongoRepository} -import scala.concurrent.Future +import javax.inject.{Inject, Singleton} +import scala.concurrent.{ExecutionContext, Future} +import scala.util.control.NonFatal @ImplementedBy(classOf[ApiFieldDefinitionsMongoRepository]) trait ApiFieldDefinitionsRepository { @@ -42,53 +48,83 @@ trait ApiFieldDefinitionsRepository { } @Singleton -class ApiFieldDefinitionsMongoRepository @Inject() (mongoDbProvider: MongoDbProvider) - extends ReactiveRepository[ApiFieldDefinitions, BSONObjectID]("fieldsDefinitions", mongoDbProvider.mongo, JsonFormatters.ApiFieldDefinitionsJF, ReactiveMongoFormats.objectIdFormats) +class ApiFieldDefinitionsMongoRepository @Inject() (mongo: MongoComponent) + (implicit ec: ExecutionContext, val mat: Materializer) + extends PlayMongoRepository[ApiFieldDefinitions]( + collectionName = "fieldsDefinitions", + mongoComponent = mongo, + domainFormat = JsonFormatters.ApiFieldDefinitionsJF, + indexes = Seq( + IndexModel(ascending(List("apiContext", "apiVersion"): _*), + IndexOptions() + .name("apiContext-apiVersion_index") + .background(true) + .unique(true)) + )) with ApiFieldDefinitionsRepository - with MongoCrudHelper[ApiFieldDefinitions] { - - override val mongoCollection: JSONCollection = collection - - override def indexes = Seq( - createCompoundIndex( - indexFieldMappings = Seq( - "apiContext" -> IndexType.Ascending, - "apiVersion" -> IndexType.Ascending - ), - indexName = Some("apiContext-apiVersion_index"), - isUnique = true - ) - ) - - override def save(definitions: ApiFieldDefinitions): Future[(ApiFieldDefinitions, IsInsert)] = { - import JsonFormatters.ApiFieldDefinitionsJF - save(definitions, selectorFor(definitions)) + with ApplicationLogger { +// with MongoCrudHelper[ApiFieldDefinitions] { + + override lazy val collection: MongoCollection[ApiFieldDefinitions] = + CollectionFactory + .collection(mongo.database, collectionName, domainFormat) + .withCodecRegistry( + fromRegistries( + fromCodecs( + Codecs.playFormatCodec(domainFormat), + Codecs.playFormatCodec(JsonFormatters.ApiContextJF), + Codecs.playFormatCodec(JsonFormatters.ApiVersionJF), + Codecs.playFormatCodec(JsonFormatters.ApiFieldDefinitionsJF), + Codecs.playFormatCodec(JsonFormatters.ValidationJF) + ), + MongoClient.DEFAULT_CODEC_REGISTRY + ) + ) + + def save(definitions: ApiFieldDefinitions): Future[(ApiFieldDefinitions, IsInsert)] = { + val query = and(equal("apiContext", Codecs.toBson(definitions.apiContext.value)), + equal("apiVersion", Codecs.toBson(definitions.apiVersion.value))) + + collection.find(query).headOption flatMap { + case Some(_: ApiFieldDefinitions) => + for { + updatedDefinitions <- collection.replaceOne( + filter = query, + replacement = definitions + ).toFuture().map(_ => definitions) + } yield (updatedDefinitions, false) + + case None => + for { + newDefinitions <- collection.insertOne(definitions).toFuture().map(_ => definitions) + } yield (newDefinitions, true) + } + +// collection +// .findOneAndUpdate(Filters.and(equal("apiContext", Codecs.toBson(definitions.apiContext.value)), +// equal("apiVersion", Codecs.toBson(definitions.apiVersion.value))), +// definitions +// ).map(_.asInstanceOf[ApiFieldDefinitions]).head } override def fetch(apiContext: ApiContext, apiVersion: ApiVersion): Future[Option[ApiFieldDefinitions]] = { - getOne(selectorFor(apiContext, apiVersion)) + collection.find(Filters.and(equal("apiContext", Codecs.toBson(apiContext.value)), + equal("apiVersion", Codecs.toBson(apiVersion.value)))).headOption() } override def fetchAll(): Future[List[ApiFieldDefinitions]] = { - getMany(Json.obj()) + collection.find().toFuture().map(_.toList) } override def delete(apiContext: ApiContext, apiVersion: ApiVersion): Future[Boolean] = { - deleteOne(selectorFor(apiContext, apiVersion)) - } - - private def selectorFor(apiContext: ApiContext, apiVersion: ApiVersion): JsObject = { - selector(apiContext, apiVersion) - } - - private def selectorFor(fd: ApiFieldDefinitions): JsObject = { - selector(fd.apiContext, fd.apiVersion) - } - - private def selector(apiContext: ApiContext, apiVersion: ApiVersion): JsObject = { - Json.obj( - "apiContext" -> apiContext.value, - "apiVersion" -> apiVersion.value - ) + collection.deleteOne(Filters.and(equal("apiContext", Codecs.toBson(apiContext.value)), + equal("apiVersion", Codecs.toBson(apiVersion.value)))) + .toFuture() + .map(_.wasAcknowledged()) + +// .head().map(result => result.getDeletedCount > 0) recoverWith { +// case NonFatal(e) => +// appLogger.error(s"Could not delete entity for apiContext ${apiContext.value} and apiVersion ${apiVersion.value}") +// Future.successful(false)} } } diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala index e63c718..49ea33a 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala @@ -14,64 +14,77 @@ * limitations under the License. */ -package uk.gov.hmrc.apisubscriptionfields.repository - -import play.api.libs.json._ -import reactivemongo.play.json._ -import reactivemongo.play.json.collection.JSONCollection - -import reactivemongo.api.Cursor - -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.Future -import uk.gov.hmrc.apisubscriptionfields.model.Types.IsInsert - -trait MongoCrudHelper[T] extends MongoIndexCreator with MongoErrorHandler { - - protected val mongoCollection: JSONCollection - - def saveAtomic(selector: JsObject, updateOperations: JsObject)(implicit w: OFormat[T]): Future[(T, IsInsert)] = { - val updateOp = mongoCollection.updateModifier( - update = updateOperations, - fetchNewObject = true, - upsert = true - ) - - mongoCollection.findAndModify(selector, updateOp).map { findAndModifyResult => - val maybeTuple: Option[(T, IsInsert)] = for { - value <- findAndModifyResult.value - updateLastError <- findAndModifyResult.lastError - } yield (value.as[T], !updateLastError.updatedExisting) - - maybeTuple.fold[(T, IsInsert)] { - handleError(selector, findAndModifyResult) - }(tuple => tuple) - } - } - - private def handleError(selector: JsObject, findAndModifyResult: mongoCollection.BatchCommands.FindAndModifyCommand.FindAndModifyResult) = { - val error = s"Error upserting database for $selector." - appLogger.error(s"$error lastError: ${findAndModifyResult.lastError}") - throw new RuntimeException(error) - } - - def save(entity: T, selector: JsObject)(implicit w: OWrites[T]): Future[(T, IsInsert)] = { - mongoCollection.update(ordered=false).one(selector, entity, upsert = true).map { updateWriteResult => (entity, handleSaveError(updateWriteResult, s"Could not save entity: $entity")) } - } - - def getMany(selector: JsObject)(implicit r: Reads[T]): Future[List[T]] = { - mongoCollection.find[JsObject, JsObject](selector, None).cursor[T]().collect[List](Int.MaxValue, Cursor.FailOnError[List[T]]()) - } - - def getOne(selector: JsObject)(implicit r: Reads[T]): Future[Option[T]] = { - mongoCollection.find[JsObject, JsObject](selector, None).one[T] - } - - def deleteOne(selector: JsObject): Future[Boolean] = { - mongoCollection.delete(ordered=false).one(selector, limit = Some(1)).map(handleDeleteError(_, s"Could not delete entity for selector: $selector")) - } - - def deleteMany(selector: JsObject): Future[Boolean] = { - mongoCollection.delete().one(selector).map(handleDeleteError(_, s"Could not delete entity for selector: $selector")) - } -} +///* +// * Copyright 2022 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.apisubscriptionfields.repository +// +//import org.mongodb.scala.MongoCollection +//import play.api.libs.json._ +// +//import scala.concurrent.ExecutionContext.Implicits.global +//import scala.concurrent.Future +//import uk.gov.hmrc.apisubscriptionfields.model.Types.IsInsert +// +//trait MongoCrudHelper[T] extends MongoIndexCreator with MongoErrorHandler { +// +// protected val collection: MongoCollection[T] +// +// def saveAtomic(selector: JsObject, updateOperations: JsObject)(implicit w: OFormat[T]): Future[(T, IsInsert)] = { +// val updateOp = collection.updateModifier( +// update = updateOperations, +// fetchNewObject = true, +// upsert = true +// ) +// +// collection.findAndModify(selector, updateOp).map { findAndModifyResult => +// val maybeTuple: Option[(T, IsInsert)] = for { +// value <- findAndModifyResult.value +// updateLastError <- findAndModifyResult.lastError +// } yield (value.as[T], !updateLastError.updatedExisting) +// +// maybeTuple.fold[(T, IsInsert)] { +// handleError(selector, findAndModifyResult) +// }(tuple => tuple) +// } +// } +// +// private def handleError(selector: JsObject, findAndModifyResult: collection.BatchCommands.FindAndModifyCommand.FindAndModifyResult) = { +// val error = s"Error upserting database for $selector." +// appLogger.error(s"$error lastError: ${findAndModifyResult.lastError}") +// throw new RuntimeException(error) +// } +// +// def save(entity: T, selector: JsObject)(implicit w: OWrites[T]): Future[(T, IsInsert)] = { +// collection.findOneAndUpdate(ordered=false).one(selector, entity, upsert = true).map { updateWriteResult => (entity, handleSaveError(updateWriteResult, s"Could not save entity: $entity")) } +// } +// +// def getMany(selector: JsObject)(implicit r: Reads[T]): Future[List[T]] = { +// collection.find[JsObject, JsObject](selector, None).cursor[T]().collect[List](Int.MaxValue, Cursor.FailOnError[List[T]]()) +// } +// +// def getOne(selector: JsObject)(implicit r: Reads[T]): Future[Option[T]] = { +// collection.find[JsObject, JsObject](selector, None).one[T] +// } +// +// def deleteOne(selector: JsObject): Future[Boolean] = { +// collection.delete(ordered=false).one(selector, limit = Some(1)).map(handleDeleteError(_, s"Could not delete entity for selector: $selector")) +// } +// +// def deleteMany(selector: JsObject): Future[Boolean] = { +// collection.delete().one(selector).map(handleDeleteError(_, s"Could not delete entity for selector: $selector")) +// } +//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala index f04a305..2f16b18 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala @@ -14,19 +14,33 @@ * limitations under the License. */ -package uk.gov.hmrc.apisubscriptionfields.repository - -import com.google.inject.ImplementedBy -import javax.inject.{Inject, Singleton} -import play.modules.reactivemongo.ReactiveMongoComponent -import reactivemongo.api.DB - -@ImplementedBy(classOf[MongoDb]) -trait MongoDbProvider { - def mongo: () => DB -} - -@Singleton -class MongoDb @Inject()(component: ReactiveMongoComponent) extends MongoDbProvider { - override val mongo: () => DB = component.mongoConnector.db -} +///* +// * Copyright 2022 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.apisubscriptionfields.repository +// +//import com.google.inject.ImplementedBy +//import javax.inject.{Inject, Singleton} +// +//@ImplementedBy(classOf[MongoDb]) +//trait MongoDbProvider { +// def mongo: () => DB +//} +// +//@Singleton +//class MongoDb @Inject()(component: ReactiveMongoComponent) extends MongoDbProvider { +// override val mongo: () => DB = component.mongoConnector.db +//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala index 25ad247..4d97280 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala @@ -14,38 +14,54 @@ * limitations under the License. */ -package uk.gov.hmrc.apisubscriptionfields.repository - -import reactivemongo.api.commands.{UpdateWriteResult, WriteResult} -import uk.gov.hmrc.apisubscriptionfields.model.Types.IsInsert -import uk.gov.hmrc.apisubscriptionfields.utils.ApplicationLogger - -trait MongoErrorHandler extends ApplicationLogger { - - def handleDeleteError(result: WriteResult, exceptionMsg: => String): Boolean = { - handleError(result, databaseAltered, exceptionMsg) - } - - def handleSaveError(updateWriteResult: UpdateWriteResult, exceptionMsg: => String): IsInsert = { - - def handleUpsertError(result: WriteResult) = - if (databaseAltered(result)) - updateWriteResult.upserted.nonEmpty - else - throw new RuntimeException(exceptionMsg) - - handleError(updateWriteResult, handleUpsertError, exceptionMsg) - } - - private def handleError(result: WriteResult, f: WriteResult => Boolean, exceptionMsg: String): Boolean = { - result.writeConcernError.fold(f(result)) { - errMsg => { - val errorMsg = s"""$exceptionMsg. $errMsg""" - appLogger.error(errorMsg) - throw new RuntimeException(errorMsg) - } - } - } - - private def databaseAltered(writeResult: WriteResult): Boolean = writeResult.n > 0 -} +///* +// * Copyright 2022 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.apisubscriptionfields.repository +// +//import com.mongodb.client.result.DeleteResult +//import uk.gov.hmrc.apisubscriptionfields.model.Types.IsInsert +//import uk.gov.hmrc.apisubscriptionfields.utils.ApplicationLogger +// +//trait MongoErrorHandler extends ApplicationLogger { +// +// def handleDeleteError(result: DeleteResult, exceptionMsg: => String): Boolean = { +// handleError(result, databaseAltered, exceptionMsg) +// } +// +// def handleSaveError(updateWriteResult: UpdateWriteResult, exceptionMsg: => String): IsInsert = { +// +// def handleUpsertError(result: WriteResult) = +// if (databaseAltered(result)) +// updateWriteResult.upserted.nonEmpty +// else +// throw new RuntimeException(exceptionMsg) +// +// handleError(updateWriteResult, handleUpsertError, exceptionMsg) +// } +// +// private def handleError(result: DeleteResult, f: DeleteResult => Boolean, exceptionMsg: String): Boolean = { +// result.writeConcernError.fold(f(result)) { +// errMsg => { +// val errorMsg = s"""$exceptionMsg. $errMsg""" +// appLogger.error(errorMsg) +// throw new RuntimeException(errorMsg) +// } +// } +// } +// +// private def databaseAltered(writeResult: WriteResult): Boolean = writeResult.n > 0 +//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala index 9d37efd..364154e 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala @@ -14,27 +14,43 @@ * limitations under the License. */ -package uk.gov.hmrc.apisubscriptionfields.repository - -import reactivemongo.api.indexes.IndexType.Ascending -import reactivemongo.api.indexes.{Index, IndexType} - - -trait MongoIndexCreator { - - def createSingleFieldAscendingIndex(indexFieldKey: String, indexName: Option[String], isUnique: Boolean): Index = { - - createCompoundIndex(indexFieldMappings = Seq(indexFieldKey -> Ascending), indexName, isUnique) - } - - def createCompoundIndex(indexFieldMappings: Seq[(String, IndexType)], indexName: Option[String], - isUnique: Boolean, isBackground: Boolean = true): Index = { - - Index( - key = indexFieldMappings, - name = indexName, - unique = isUnique, - background = isBackground - ) - } -} +///* +// * Copyright 2022 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.apisubscriptionfields.repository +// +//import reactivemongo.api.indexes.IndexType.Ascending +//import reactivemongo.api.indexes.{Index, IndexType} +// +// +//trait MongoIndexCreator { +// +// def createSingleFieldAscendingIndex(indexFieldKey: String, indexName: Option[String], isUnique: Boolean): Index = { +// +// createCompoundIndex(indexFieldMappings = Seq(indexFieldKey -> Ascending), indexName, isUnique) +// } +// +// def createCompoundIndex(indexFieldMappings: Seq[(String, IndexType)], indexName: Option[String], +// isUnique: Boolean, isBackground: Boolean = true): Index = { +// +// Index( +// key = indexFieldMappings, +// name = indexName, +// unique = isUnique, +// background = isBackground +// ) +// } +//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala index b09f6f4..30f22b1 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala @@ -17,19 +17,21 @@ package uk.gov.hmrc.apisubscriptionfields.repository import javax.inject.{Inject, Singleton} - import com.google.inject.ImplementedBy -import reactivemongo.api.indexes.IndexType -import reactivemongo.bson.BSONObjectID -import reactivemongo.play.json._ -import reactivemongo.play.json.collection.JSONCollection import uk.gov.hmrc.apisubscriptionfields.model._ import Types.IsInsert -import uk.gov.hmrc.mongo.ReactiveRepository -import uk.gov.hmrc.mongo.json.ReactiveMongoFormats -import scala.concurrent.Future +import akka.stream.Materializer +import org.bson.codecs.configuration.CodecRegistries.{fromCodecs, fromRegistries} +import org.mongodb.scala.model.Filters.{and, equal} +import org.mongodb.scala.{MongoClient, MongoCollection} +import org.mongodb.scala.model.{Filters, IndexModel, IndexOptions} +import org.mongodb.scala.model.Indexes.ascending +import uk.gov.hmrc.mongo.MongoComponent + +import scala.concurrent.{ExecutionContext, Future} import play.api.libs.json.Json import play.api.libs.json.JsObject +import uk.gov.hmrc.mongo.play.json.{Codecs, CollectionFactory, PlayMongoRepository} @ImplementedBy(classOf[SubscriptionFieldsMongoRepository]) trait SubscriptionFieldsRepository { @@ -48,89 +50,152 @@ trait SubscriptionFieldsRepository { @Singleton -class SubscriptionFieldsMongoRepository @Inject()(mongoDbProvider: MongoDbProvider) - extends ReactiveRepository[SubscriptionFields, BSONObjectID]( - "subscriptionFields", - mongoDbProvider.mongo, - JsonFormatters.SubscriptionFieldsJF, - ReactiveMongoFormats.objectIdFormats - ) +class SubscriptionFieldsMongoRepository @Inject()(mongo: MongoComponent) + (implicit ec: ExecutionContext, val mat: Materializer) + extends PlayMongoRepository[SubscriptionFields]( + collectionName = "subscriptionFields", + mongoComponent = mongo, + domainFormat = JsonFormatters.SubscriptionFieldsJF, + indexes = Seq( + IndexModel(ascending(List("clientId", "apiContext", "apiVersion"): _*), + IndexOptions() + .name("clientId-apiContext-apiVersion_Index") + .unique(true)), + IndexModel(ascending("clientId"), + IndexOptions() + .name("clientIdIndex") + .unique(false)), + IndexModel(ascending("fieldsId"), + IndexOptions() + .name("fieldsIdIndex") + .unique(true)) + )) +// { + +// extends ReactiveRepository[SubscriptionFields, BSONObjectID]( +// "subscriptionFields", +// mongoDbProvider.mongo, +// JsonFormatters.SubscriptionFieldsJF, +// ReactiveMongoFormats.objectIdFormats +// ) with SubscriptionFieldsRepository - with MongoCrudHelper[SubscriptionFields] +// with MongoCrudHelper[SubscriptionFields] with JsonFormatters { +// +// override val mongoCollection: JSONCollection = collection +// +// override def indexes = Seq( +// createCompoundIndex( +// indexFieldMappings = Seq( +// "clientId" -> IndexType.Ascending, +// "apiContext" -> IndexType.Ascending, +// "apiVersion" -> IndexType.Ascending +// ), +// indexName = Some("clientId-apiContext-apiVersion_Index"), +// isUnique = true +// ), +// createSingleFieldAscendingIndex( +// indexFieldKey = "clientId", +// indexName = Some("clientIdIndex"), +// isUnique = false +// ), +// createSingleFieldAscendingIndex( +// indexFieldKey = "fieldsId", +// indexName = Some("fieldsIdIndex"), +// isUnique = true +// ) +// ) + + override lazy val collection: MongoCollection[SubscriptionFields] = + CollectionFactory + .collection(mongo.database, collectionName, domainFormat) + .withCodecRegistry( + fromRegistries( + fromCodecs( + Codecs.playFormatCodec(domainFormat), + Codecs.playFormatCodec(JsonFormatters.ApiContextJF), + Codecs.playFormatCodec(JsonFormatters.ApiVersionJF), + Codecs.playFormatCodec(JsonFormatters.SubscriptionFieldsIdjsonFormat), + Codecs.playFormatCodec(JsonFormatters.ValidationJF) + ), + MongoClient.DEFAULT_CODEC_REGISTRY + ) + ) - override val mongoCollection: JSONCollection = collection - - override def indexes = Seq( - createCompoundIndex( - indexFieldMappings = Seq( - "clientId" -> IndexType.Ascending, - "apiContext" -> IndexType.Ascending, - "apiVersion" -> IndexType.Ascending - ), - indexName = Some("clientId-apiContext-apiVersion_Index"), - isUnique = true - ), - createSingleFieldAscendingIndex( - indexFieldKey = "clientId", - indexName = Some("clientIdIndex"), - isUnique = false - ), - createSingleFieldAscendingIndex( - indexFieldKey = "fieldsId", - indexName = Some("fieldsIdIndex"), - isUnique = true - ) - ) override def saveAtomic(subscription: SubscriptionFields): Future[(SubscriptionFields, IsInsert)] = { - saveAtomic( - selector = subscriptionFieldsSelector(subscription), - updateOperations = Json.obj( - "$setOnInsert" -> Json.obj("fieldsId" -> subscription.fieldsId), - "$set" -> Json.obj("fields" -> Json.toJson(subscription.fields)) - ) - ) + val query = and(equal("clientId", Codecs.toBson(subscription.clientId.value)), + equal("apiContext", Codecs.toBson(subscription.apiContext.value)), + equal("apiVersion", Codecs.toBson(subscription.apiVersion.value))) + + + collection.find(query).headOption flatMap { + case Some(_: SubscriptionFields) => + for { + updatedDefinitions <- collection.replaceOne( + filter = query, + replacement = subscription + ).toFuture().map(_ => subscription) + } yield (updatedDefinitions, false) + + case None => + for { + newSubscriptionFields <- collection.insertOne(subscription).toFuture().map(_ => subscription) + } yield (newSubscriptionFields, true) + } +// +// +// saveAtomic( +// selector = subscriptionFieldsSelector(subscription), +// updateOperations = Json.obj( +// "$setOnInsert" -> Json.obj("fieldsId" -> subscription.fieldsId), +// "$set" -> Json.obj("fields" -> Json.toJson(subscription.fields)) +// ) +// ) +// +// collection +// .findOneAndReplace(Filters.and(equal("apiContext", Codecs.toBson(definitions.apiContext.value)), +// equal("apiVersion", Codecs.toBson(definitions.apiVersion.value))), +// definitions +// ).map(_.asInstanceOf[ApiFieldDefinitions]).head } override def fetch(clientId: ClientId, apiContext: ApiContext, apiVersion: ApiVersion): Future[Option[SubscriptionFields]] = { - getOne(subscriptionFieldsSelector(clientId, apiContext, apiVersion)) + val query = and(equal("clientId", Codecs.toBson(clientId.value)), + equal("apiContext", Codecs.toBson(apiContext.value)), + equal("apiVersion", Codecs.toBson(apiVersion.value))) + + collection.find(query).headOption() } override def fetchByFieldsId(fieldsId: SubscriptionFieldsId): Future[Option[SubscriptionFields]] = { - getOne(fieldsIdSelector(fieldsId)) + val query = and(equal("fieldsId", Codecs.toBson(fieldsId.value))) + collection.find(query).headOption() } override def fetchByClientId(clientId: ClientId): Future[List[SubscriptionFields]] = { - getMany(clientIdSelector(clientId)) + val query = equal("clientId", Codecs.toBson(clientId.value)) + collection.find(query).toFuture().map(_.toList) } override def fetchAll: Future[List[SubscriptionFields]] = { - getMany(Json.obj()) + collection.find().toFuture().map(_.toList) } override def delete(clientId: ClientId, apiContext: ApiContext, apiVersion: ApiVersion): Future[Boolean] = { - deleteOne(subscriptionFieldsSelector(clientId, apiContext, apiVersion)) + val query = and(equal("clientId", Codecs.toBson(clientId.value)), + equal("apiContext", Codecs.toBson(apiContext.value)), + equal("apiVersion", Codecs.toBson(apiVersion.value))) + collection.deleteOne(query) + .toFuture() + .map(_.wasAcknowledged()) } override def delete(clientId: ClientId): Future[Boolean] = { - deleteMany(clientIdSelector(clientId)) + val query = equal("clientId", Codecs.toBson(clientId.value)) + collection.deleteMany(query) + .toFuture() + .map(_.wasAcknowledged()) } - private def clientIdSelector(clientId: ClientId) = Json.obj("clientId" -> clientId.value) - - private def fieldsIdSelector(fieldsId: SubscriptionFieldsId) = Json.obj("fieldsId" -> fieldsId.value) - - private def subscriptionFieldsSelector(clientId: ClientId, apiContext: ApiContext, apiVersion: ApiVersion): JsObject = { - Json.obj( - "clientId" -> clientId.value, - "apiContext" -> apiContext.value, - "apiVersion" -> apiVersion.value - ) - } - - private def subscriptionFieldsSelector(subscription: SubscriptionFields): JsObject = subscriptionFieldsSelector( - subscription.clientId, subscription.apiContext, subscription.apiVersion - ) - } diff --git a/conf/application.conf b/conf/application.conf index e0a37fe..75b83cc 100644 --- a/conf/application.conf +++ b/conf/application.conf @@ -41,7 +41,7 @@ play.http.errorHandler = "uk.gov.hmrc.play.bootstrap.backend.http.JsonErrorHandl # Play Modules # ~~~~ # Additional play modules can be added here -play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoHmrcModule" +play.modules.enabled += "uk.gov.hmrc.mongo.play.PlayMongoModule" play.modules.enabled += "uk.gov.hmrc.play.bootstrap.HttpClientModule" play.modules.enabled += "uk.gov.hmrc.play.http.metrics.Module" diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala index 20539c3..3f9735d 100644 --- a/project/AppDependencies.scala +++ b/project/AppDependencies.scala @@ -9,18 +9,29 @@ object AppDependencies { private lazy val dependencies = Seq( "uk.gov.hmrc" %% "bootstrap-backend-play-28" % bootstrapVersion, - "uk.gov.hmrc" %% "simple-reactivemongo" % "8.1.0-play-28", + "uk.gov.hmrc.mongo" %% "hmrc-mongo-play-28" % "0.68.0", "org.julienrf" %% "play-json-derived-codecs" % "6.0.0", "com.typesafe.play" %% "play-json" % "2.9.2", "uk.gov.hmrc" %% "http-metrics" % "2.5.0-play-28", "org.typelevel" %% "cats-core" % "2.6.1", "eu.timepit" %% "refined" % "0.9.13", "be.venneborg" %% "play28-refined" % "0.6.0", - "commons-validator" % "commons-validator" % "1.6" + "commons-validator" % "commons-validator" % "1.6", + "com.beachape" %% "enumeratum-play-json" % "1.6.0" ) private lazy val testDependencies = Seq( - "uk.gov.hmrc" %% "reactivemongo-test" % "5.1.0-play-28", +// "uk.gov.hmrc" %% "bootstrap-test-play-28" % bootstrapVersion, +// "uk.gov.hmrc.mongo" %% "hmrc-mongo-test-play-28" % mongoVersion, +// "org.mockito" %% "mockito-scala-scalatest" % "1.16.46", +// "org.jsoup" % "jsoup" % "1.13.1", +// "org.scalaj" %% "scalaj-http" % "2.4.2", +// "com.github.tomakehurst" % "wiremock-jre8-standalone" % "2.31.0", +// "org.scalacheck" %% "scalacheck" % "1.15.4", +// "org.scalatestplus" %% "scalacheck-1-15" % "3.2.10.0" + + "uk.gov.hmrc" %% "bootstrap-test-play-28" % bootstrapVersion, + "uk.gov.hmrc.mongo" %% "hmrc-mongo-test-play-28" % "0.68.0", "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.3", "org.mockito" %% "mockito-scala-scalatest" % "1.16.42", "org.pegdown" % "pegdown" % "1.6.0", diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala index 92b4a68..306da96 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala @@ -16,58 +16,53 @@ package uk.gov.hmrc.apisubscriptionfields.repository -import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach} -import reactivemongo.api.DB -import reactivemongo.bson.BSONDocument -import uk.gov.hmrc.apisubscriptionfields.model.{ApiContext, ApiFieldDefinitions, JsonFormatters} -import uk.gov.hmrc.apisubscriptionfields.FieldDefinitionTestData -import uk.gov.hmrc.mongo.MongoSpecSupport -import uk.gov.hmrc.apisubscriptionfields.AsyncHmrcSpec +import org.mongodb.scala.model.Filters +import org.scalatest.matchers.should.Matchers +import org.scalatest.wordspec.AnyWordSpec +import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, OptionValues} +import org.scalatestplus.play.guice.GuiceOneAppPerSuite +import play.api.test.{DefaultAwaitTimeout, FutureAwaits} +import uk.gov.hmrc.apisubscriptionfields.model.{ApiContext, ApiFieldDefinitions, ApiVersion, JsonFormatters} +import uk.gov.hmrc.apisubscriptionfields.SubscriptionFieldsTestData.{FakeContext, FakeVersion, NelOfFieldDefinitions, uniqueApiContext} +import uk.gov.hmrc.mongo.play.json.Codecs + import scala.concurrent.ExecutionContext.Implicits.global -class ApiFieldDefinitionsRepositorySpec extends AsyncHmrcSpec - with BeforeAndAfterAll - with BeforeAndAfterEach - with MongoSpecSupport - with JsonFormatters - with FieldDefinitionTestData - { self => +class ApiFieldDefinitionsRepositorySpec extends AnyWordSpec + with GuiceOneAppPerSuite + with Matchers + with OptionValues + with DefaultAwaitTimeout + with FutureAwaits + with BeforeAndAfterEach { - private val mongoDbProvider = new MongoDbProvider { - override val mongo: () => DB = self.mongo - } + private val repository = app.injector.instanceOf[ApiFieldDefinitionsMongoRepository] - private val repository = new ApiFieldDefinitionsMongoRepository(mongoDbProvider) +// private val repository = new ApiFieldDefinitionsMongoRepository(mongoDbProvider) - override def beforeEach() { + override protected def beforeEach() { super.beforeEach() - await(repository.drop) - } - - override def afterAll() { - super.afterAll() - await(repository.drop) + await(repository.collection.drop.toFuture()) } - private def collectionSize: Int = { - await(repository.collection.count()) + def collectionSize: Long = { + await(repository.collection.countDocuments().toFuture()) } - private def createApiFieldDefinitions = ApiFieldDefinitions(FakeContext, FakeVersion, NelOfFieldDefinitions) + def createApiFieldDefinitions(apiContext: ApiContext = FakeContext) = ApiFieldDefinitions(apiContext = FakeContext, FakeVersion, NelOfFieldDefinitions) - private trait Setup { - val definitions: ApiFieldDefinitions = createApiFieldDefinitions + trait Setup { + val definitions: ApiFieldDefinitions = createApiFieldDefinitions() } "save" should { - import reactivemongo.play.json._ "insert the record in the collection" in new Setup { collectionSize shouldBe 0 await(repository.save(definitions)) shouldBe ((definitions, true)) collectionSize shouldBe 1 - await(repository.collection.find(selector(definitions)).one[ApiFieldDefinitions]) shouldBe Some(definitions) + await(repository.collection.find(selector(definitions)).toFuture()) shouldBe Some(definitions) } "update the record in the collection" in new Setup { @@ -79,7 +74,7 @@ class ApiFieldDefinitionsRepositorySpec extends AsyncHmrcSpec val edited = definitions.copy(fieldDefinitions = NelOfFieldDefinitions) await(repository.save(edited)) shouldBe ((edited, false)) collectionSize shouldBe 1 - await(repository.collection.find(selector(edited)).one[ApiFieldDefinitions]) shouldBe Some(edited) + await(repository.collection.find(selector(edited)).toFuture()) shouldBe Some(edited) } } @@ -120,7 +115,7 @@ class ApiFieldDefinitionsRepositorySpec extends AsyncHmrcSpec "delete" should { "remove the record with a specific fields definition" in { - val definitions = createApiFieldDefinitions + val definitions = createApiFieldDefinitions() await(repository.save(definitions)) collectionSize shouldBe 1 @@ -130,7 +125,7 @@ class ApiFieldDefinitionsRepositorySpec extends AsyncHmrcSpec } "not alter the collection for unknown fields definition" in { - await(repository.save(createApiFieldDefinitions)) + await(repository.save(createApiFieldDefinitions())) collectionSize shouldBe 1 await(repository.delete(ApiContext("DOES_NOT_EXIST"), FakeVersion)) shouldBe false @@ -150,6 +145,7 @@ class ApiFieldDefinitionsRepositorySpec extends AsyncHmrcSpec } private def selector(fd: ApiFieldDefinitions) = { - BSONDocument("apiContext" -> fd.apiContext.value, "apiVersion" -> fd.apiVersion.value) + Filters.and(Filters.equal("apiContext", Codecs.toBson(fd.apiContext.value)), + Filters.equal("apiVersion", Codecs.toBson(fd.apiVersion.value))) } } diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala index 49d966c..e61e250 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala @@ -14,95 +14,111 @@ * limitations under the License. */ -package uk.gov.hmrc.apisubscriptionfields.repository - -import reactivemongo.api.commands._ -import reactivemongo.bson.BSONInteger -import uk.gov.hmrc.apisubscriptionfields.AsyncHmrcSpec - -class MongoErrorHandlerSpec extends AsyncHmrcSpec { - - private val mongoErrorHandler = new MongoErrorHandler {} - private val BsonValue = BSONInteger(1) - private val SomeWriteConcernError = Some(WriteConcernError(1, "ERROR")) - - "handleDeleteError" should { - "return true if there are no database errors and at least one record deleted" in { - val successfulWriteResult = writeResult(alteredRecords = 1) - - mongoErrorHandler.handleDeleteError(successfulWriteResult, "ERROR_MSG") shouldBe true - } - - "return false if there are no database errors and no record deleted" in { - val noDeletedRecordsWriteResult = writeResult(alteredRecords = 0) - - mongoErrorHandler.handleDeleteError(noDeletedRecordsWriteResult, "ERROR_MSG") shouldBe false - } - - "throw a RuntimeException if there is a database error" in { - val writeConcernError = Some(WriteConcernError(1, "ERROR")) - val errorWriteResult = writeResult(alteredRecords = 0, writeConcernError = writeConcernError) - - val caught = intercept[RuntimeException](mongoErrorHandler.handleDeleteError(errorWriteResult, "ERROR_MSG")) - - caught.getMessage shouldBe "ERROR_MSG. WriteConcernError(1,ERROR)" - } - } - - "handleSaveError" should { - "return true if there are no database errors and at least one record inserted" in { - val Inserted = Seq(Upserted(0, BsonValue)) - val successfulInsertWriteResult = updateWriteResult(alteredRecords = 1, upserted = Inserted) - - mongoErrorHandler.handleSaveError(successfulInsertWriteResult, "ERROR_MSG") shouldBe true - } - - "return false if there are no database errors and no record deleted and at least one record updated" in { - val successfulUpdateWriteResult = updateWriteResult(alteredRecords = 1) - - mongoErrorHandler.handleSaveError(successfulUpdateWriteResult, "ERROR_MSG") shouldBe false - } - - "throw a RuntimeException if there is a database error" in { - val errorUpdateWriteResult = updateWriteResult(alteredRecords = 0, writeConcernError = SomeWriteConcernError) - - val caught = intercept[RuntimeException](mongoErrorHandler.handleSaveError(errorUpdateWriteResult, "ERROR_MSG")) - - caught.getMessage shouldBe "ERROR_MSG. WriteConcernError(1,ERROR)" - } - - "throw a RuntimeException if there are no records altered" in { - val errorUpdateWriteResult = updateWriteResult(alteredRecords = 0) - - val caught = intercept[RuntimeException](mongoErrorHandler.handleSaveError(errorUpdateWriteResult, "ERROR_MSG")) - - caught.getMessage shouldBe "ERROR_MSG" - } - - } - - private def writeResult(alteredRecords: Int, writeErrors: Seq[WriteError] = Nil, - writeConcernError: Option[WriteConcernError] = None) = { - DefaultWriteResult( - ok = true, - n = alteredRecords, - writeErrors = writeErrors, - writeConcernError = writeConcernError, - code = None, - errmsg = None) - } - - private def updateWriteResult(alteredRecords: Int, upserted: Seq[Upserted] = Nil, writeErrors: Seq[WriteError] = Nil, - writeConcernError: Option[WriteConcernError] = None) = { - UpdateWriteResult( - ok = true, - n = alteredRecords, - nModified = 0, - upserted = upserted, - writeErrors = writeErrors, - writeConcernError = writeConcernError, - code = None, - errmsg = None) - } - -} +///* +// * Copyright 2022 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.apisubscriptionfields.repository +// +//import reactivemongo.api.commands._ +//import reactivemongo.bson.BSONInteger +//import uk.gov.hmrc.apisubscriptionfields.AsyncHmrcSpec +// +//class MongoErrorHandlerSpec extends AsyncHmrcSpec { +// +// private val mongoErrorHandler = new MongoErrorHandler {} +// private val BsonValue = BSONInteger(1) +// private val SomeWriteConcernError = Some(WriteConcernError(1, "ERROR")) +// +// "handleDeleteError" should { +// "return true if there are no database errors and at least one record deleted" in { +// val successfulWriteResult = writeResult(alteredRecords = 1) +// +// mongoErrorHandler.handleDeleteError(successfulWriteResult, "ERROR_MSG") shouldBe true +// } +// +// "return false if there are no database errors and no record deleted" in { +// val noDeletedRecordsWriteResult = writeResult(alteredRecords = 0) +// +// mongoErrorHandler.handleDeleteError(noDeletedRecordsWriteResult, "ERROR_MSG") shouldBe false +// } +// +// "throw a RuntimeException if there is a database error" in { +// val writeConcernError = Some(WriteConcernError(1, "ERROR")) +// val errorWriteResult = writeResult(alteredRecords = 0, writeConcernError = writeConcernError) +// +// val caught = intercept[RuntimeException](mongoErrorHandler.handleDeleteError(errorWriteResult, "ERROR_MSG")) +// +// caught.getMessage shouldBe "ERROR_MSG. WriteConcernError(1,ERROR)" +// } +// } +// +// "handleSaveError" should { +// "return true if there are no database errors and at least one record inserted" in { +// val Inserted = Seq(Upserted(0, BsonValue)) +// val successfulInsertWriteResult = updateWriteResult(alteredRecords = 1, upserted = Inserted) +// +// mongoErrorHandler.handleSaveError(successfulInsertWriteResult, "ERROR_MSG") shouldBe true +// } +// +// "return false if there are no database errors and no record deleted and at least one record updated" in { +// val successfulUpdateWriteResult = updateWriteResult(alteredRecords = 1) +// +// mongoErrorHandler.handleSaveError(successfulUpdateWriteResult, "ERROR_MSG") shouldBe false +// } +// +// "throw a RuntimeException if there is a database error" in { +// val errorUpdateWriteResult = updateWriteResult(alteredRecords = 0, writeConcernError = SomeWriteConcernError) +// +// val caught = intercept[RuntimeException](mongoErrorHandler.handleSaveError(errorUpdateWriteResult, "ERROR_MSG")) +// +// caught.getMessage shouldBe "ERROR_MSG. WriteConcernError(1,ERROR)" +// } +// +// "throw a RuntimeException if there are no records altered" in { +// val errorUpdateWriteResult = updateWriteResult(alteredRecords = 0) +// +// val caught = intercept[RuntimeException](mongoErrorHandler.handleSaveError(errorUpdateWriteResult, "ERROR_MSG")) +// +// caught.getMessage shouldBe "ERROR_MSG" +// } +// +// } +// +// private def writeResult(alteredRecords: Int, writeErrors: Seq[WriteError] = Nil, +// writeConcernError: Option[WriteConcernError] = None) = { +// DefaultWriteResult( +// ok = true, +// n = alteredRecords, +// writeErrors = writeErrors, +// writeConcernError = writeConcernError, +// code = None, +// errmsg = None) +// } +// +// private def updateWriteResult(alteredRecords: Int, upserted: Seq[Upserted] = Nil, writeErrors: Seq[WriteError] = Nil, +// writeConcernError: Option[WriteConcernError] = None) = { +// UpdateWriteResult( +// ok = true, +// n = alteredRecords, +// nModified = 0, +// upserted = upserted, +// writeErrors = writeErrors, +// writeConcernError = writeConcernError, +// code = None, +// errmsg = None) +// } +// +//} diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala index f730484..56eeecd 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala @@ -17,48 +17,41 @@ package uk.gov.hmrc.apisubscriptionfields.repository import java.util.UUID - -import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach} -import reactivemongo.api.DB -import reactivemongo.bson._ +import org.scalatest.{BeforeAndAfterAll, BeforeAndAfterEach, OptionValues} import uk.gov.hmrc.apisubscriptionfields.model._ import Types._ +import org.mongodb.scala.model.Updates.set +import org.mongodb.scala.model.{Filters, FindOneAndUpdateOptions, ReturnDocument} +import org.scalatest.matchers.should.Matchers +import org.scalatestplus.play.guice.GuiceOneAppPerSuite +import play.api.test.{DefaultAwaitTimeout, FutureAwaits} import uk.gov.hmrc.apisubscriptionfields.SubscriptionFieldsTestData -import uk.gov.hmrc.mongo.MongoSpecSupport import uk.gov.hmrc.apisubscriptionfields.AsyncHmrcSpec +import uk.gov.hmrc.apisubscriptionfields.SubscriptionFieldsTestData.{FakeClientId, FakeClientId2, FakeContext, FakeContext2, FakeVersion, createSubscriptionFieldsWithApiContext, fakeRawContext2, fieldN, uniqueClientId} +import uk.gov.hmrc.mongo.play.json.Codecs import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.Future class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec - with BeforeAndAfterAll - with BeforeAndAfterEach - with MongoSpecSupport - with JsonFormatters - with SubscriptionFieldsTestData { self => - - private val mongoDbProvider = new MongoDbProvider { - override val mongo: () => DB = self.mongo - } - - private val repository = new SubscriptionFieldsMongoRepository(mongoDbProvider) { + with GuiceOneAppPerSuite + with Matchers + with OptionValues + with DefaultAwaitTimeout + with FutureAwaits + with BeforeAndAfterEach { - import play.api.libs.json._ +// private val mongoDbProvider = new MongoDbProvider { +// override val mongo: () => DB = self.mongo +// } + private val repository = app.injector.instanceOf[SubscriptionFieldsMongoRepository] - def saveByFieldsId(subscription: SubscriptionFields): Future[(SubscriptionFields, IsInsert)] = { - save(subscription, Json.obj("fieldsId" -> subscription.fieldsId.value)) - } - } + import play.api.libs.json._ override def beforeEach() { super.beforeEach() - await(repository.drop) - } - - override def afterAll() { - super.afterAll() - await(repository.drop) + await(repository.collection.drop.toFuture()) } private def createApiSubscriptionFields(clientId: ClientId = FakeClientId): SubscriptionFields = { @@ -66,25 +59,33 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec SubscriptionFields(clientId, FakeContext, FakeVersion, SubscriptionFieldsId(UUID.randomUUID()), fields) } - private def collectionSize: Int = { - await(repository.collection.count()) + private def collectionSize: Long = { + await(repository.collection.countDocuments().toFuture()) } private def selector(s: SubscriptionFields) = { - BSONDocument("clientId" -> s.clientId.value, "apiContext" -> s.apiContext.value, "apiVersion" -> s.apiVersion.value) + Filters.and(Filters.equal("apiContext", Codecs.toBson(s.apiContext.value)), + Filters.equal("apiVersion", Codecs.toBson(s.apiVersion.value)), + Filters.equal("clientId", Codecs.toBson(s.apiVersion.value))) + } + + def saveByFieldsId(subscription: SubscriptionFields): Future[SubscriptionFields] = { + val query = Filters.equal("fieldsId", Codecs.toBson(subscription.fieldsId.value)) + repository.collection.findOneAndUpdate(filter = query, + update = set("fieldsId", Codecs.toBson(subscription.fieldsId.value)), + options = FindOneAndUpdateOptions().upsert(false).returnDocument(ReturnDocument.AFTER) + ).toFuture() } "saveAtomic" should { val apiSubscriptionFields = createApiSubscriptionFields() - import reactivemongo.play.json._ - "insert the record in the collection" in { collectionSize shouldBe 0 await(repository.saveAtomic(apiSubscriptionFields)) shouldBe ((apiSubscriptionFields, true)) collectionSize shouldBe 1 - await(repository.collection.find(selector(apiSubscriptionFields)).one[SubscriptionFields]) shouldBe Some(apiSubscriptionFields) + await(repository.collection.find(selector(apiSubscriptionFields)).toFuture()) shouldBe Some(apiSubscriptionFields) } "update the record in the collection" in { @@ -96,7 +97,7 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec val edited = apiSubscriptionFields.copy(fields = Map(fieldN(4) -> "value_4")) await(repository.saveAtomic(edited)) shouldBe ((edited, false)) collectionSize shouldBe 1 - await(repository.collection.find(selector(edited)).one[SubscriptionFields]) shouldBe Some(edited) + await(repository.collection.find(selector(edited)).toFuture()) shouldBe Some(edited) } } @@ -239,7 +240,7 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec await(repository.saveAtomic(apiSubscription)) collectionSize shouldBe 1 - await(repository.saveByFieldsId(apiSubscription.copy(apiVersion = ApiVersion("2.2")))) + await(saveByFieldsId(apiSubscription.copy(apiVersion = ApiVersion("2.2")))) collectionSize shouldBe 1 } From 6a82321bf5dca578cf31bf23410c3cb0c8a543bd Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Wed, 12 Oct 2022 15:38:04 +0100 Subject: [PATCH 2/7] APID-520 - fixing tests --- .../repository/FieldDefinitionRepository.scala | 14 ++------------ .../SubscriptionFieldsRepository.scala | 18 ++---------------- .../ApiFieldDefinitionsRepositorySpec.scala | 11 ++++++++--- .../SubscriptionFieldsRepositorySpec.scala | 6 +++--- 4 files changed, 15 insertions(+), 34 deletions(-) diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala index 1a427d7..7ac7eee 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala @@ -99,12 +99,6 @@ class ApiFieldDefinitionsMongoRepository @Inject() (mongo: MongoComponent) newDefinitions <- collection.insertOne(definitions).toFuture().map(_ => definitions) } yield (newDefinitions, true) } - -// collection -// .findOneAndUpdate(Filters.and(equal("apiContext", Codecs.toBson(definitions.apiContext.value)), -// equal("apiVersion", Codecs.toBson(definitions.apiVersion.value))), -// definitions -// ).map(_.asInstanceOf[ApiFieldDefinitions]).head } override def fetch(apiContext: ApiContext, apiVersion: ApiVersion): Future[Option[ApiFieldDefinitions]] = { @@ -117,14 +111,10 @@ class ApiFieldDefinitionsMongoRepository @Inject() (mongo: MongoComponent) } override def delete(apiContext: ApiContext, apiVersion: ApiVersion): Future[Boolean] = { + appLogger.info(s"delete apiContext value ${apiContext.value} and apiVersion value ${apiVersion.value}") collection.deleteOne(Filters.and(equal("apiContext", Codecs.toBson(apiContext.value)), equal("apiVersion", Codecs.toBson(apiVersion.value)))) .toFuture() - .map(_.wasAcknowledged()) - -// .head().map(result => result.getDeletedCount > 0) recoverWith { -// case NonFatal(e) => -// appLogger.error(s"Could not delete entity for apiContext ${apiContext.value} and apiVersion ${apiVersion.value}") -// Future.successful(false)} + .map(_.getDeletedCount > 0) } } diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala index 30f22b1..bb1d41b 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala @@ -31,6 +31,7 @@ import uk.gov.hmrc.mongo.MongoComponent import scala.concurrent.{ExecutionContext, Future} import play.api.libs.json.Json import play.api.libs.json.JsObject +import uk.gov.hmrc.apisubscriptionfields.utils.ApplicationLogger import uk.gov.hmrc.mongo.play.json.{Codecs, CollectionFactory, PlayMongoRepository} @ImplementedBy(classOf[SubscriptionFieldsMongoRepository]) @@ -79,6 +80,7 @@ class SubscriptionFieldsMongoRepository @Inject()(mongo: MongoComponent) // ReactiveMongoFormats.objectIdFormats // ) with SubscriptionFieldsRepository + with ApplicationLogger // with MongoCrudHelper[SubscriptionFields] with JsonFormatters { // @@ -128,7 +130,6 @@ class SubscriptionFieldsMongoRepository @Inject()(mongo: MongoComponent) equal("apiContext", Codecs.toBson(subscription.apiContext.value)), equal("apiVersion", Codecs.toBson(subscription.apiVersion.value))) - collection.find(query).headOption flatMap { case Some(_: SubscriptionFields) => for { @@ -143,21 +144,6 @@ class SubscriptionFieldsMongoRepository @Inject()(mongo: MongoComponent) newSubscriptionFields <- collection.insertOne(subscription).toFuture().map(_ => subscription) } yield (newSubscriptionFields, true) } -// -// -// saveAtomic( -// selector = subscriptionFieldsSelector(subscription), -// updateOperations = Json.obj( -// "$setOnInsert" -> Json.obj("fieldsId" -> subscription.fieldsId), -// "$set" -> Json.obj("fields" -> Json.toJson(subscription.fields)) -// ) -// ) -// -// collection -// .findOneAndReplace(Filters.and(equal("apiContext", Codecs.toBson(definitions.apiContext.value)), -// equal("apiVersion", Codecs.toBson(definitions.apiVersion.value))), -// definitions -// ).map(_.asInstanceOf[ApiFieldDefinitions]).head } override def fetch(clientId: ClientId, apiContext: ApiContext, apiVersion: ApiVersion): Future[Option[SubscriptionFields]] = { diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala index 306da96..f1d5c94 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala @@ -45,11 +45,16 @@ class ApiFieldDefinitionsRepositorySpec extends AnyWordSpec await(repository.collection.drop.toFuture()) } + override protected def afterEach() { + super.afterEach() + await(repository.collection.drop.toFuture()) + } + def collectionSize: Long = { await(repository.collection.countDocuments().toFuture()) } - def createApiFieldDefinitions(apiContext: ApiContext = FakeContext) = ApiFieldDefinitions(apiContext = FakeContext, FakeVersion, NelOfFieldDefinitions) + def createApiFieldDefinitions(apiContext: ApiContext = FakeContext) = ApiFieldDefinitions(apiContext, FakeVersion, NelOfFieldDefinitions) trait Setup { val definitions: ApiFieldDefinitions = createApiFieldDefinitions() @@ -62,7 +67,7 @@ class ApiFieldDefinitionsRepositorySpec extends AnyWordSpec await(repository.save(definitions)) shouldBe ((definitions, true)) collectionSize shouldBe 1 - await(repository.collection.find(selector(definitions)).toFuture()) shouldBe Some(definitions) + await(repository.collection.find(selector(definitions)).headOption()) shouldBe Some(definitions) } "update the record in the collection" in new Setup { @@ -74,7 +79,7 @@ class ApiFieldDefinitionsRepositorySpec extends AnyWordSpec val edited = definitions.copy(fieldDefinitions = NelOfFieldDefinitions) await(repository.save(edited)) shouldBe ((edited, false)) collectionSize shouldBe 1 - await(repository.collection.find(selector(edited)).toFuture()) shouldBe Some(edited) + await(repository.collection.find(selector(edited)).headOption()) shouldBe Some(edited) } } diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala index 56eeecd..3a996b9 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala @@ -66,7 +66,7 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec private def selector(s: SubscriptionFields) = { Filters.and(Filters.equal("apiContext", Codecs.toBson(s.apiContext.value)), Filters.equal("apiVersion", Codecs.toBson(s.apiVersion.value)), - Filters.equal("clientId", Codecs.toBson(s.apiVersion.value))) + Filters.equal("clientId", Codecs.toBson(s.clientId.value))) } def saveByFieldsId(subscription: SubscriptionFields): Future[SubscriptionFields] = { @@ -85,7 +85,7 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec await(repository.saveAtomic(apiSubscriptionFields)) shouldBe ((apiSubscriptionFields, true)) collectionSize shouldBe 1 - await(repository.collection.find(selector(apiSubscriptionFields)).toFuture()) shouldBe Some(apiSubscriptionFields) + await(repository.collection.find(selector(apiSubscriptionFields)).headOption()) shouldBe Some(apiSubscriptionFields) } "update the record in the collection" in { @@ -97,7 +97,7 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec val edited = apiSubscriptionFields.copy(fields = Map(fieldN(4) -> "value_4")) await(repository.saveAtomic(edited)) shouldBe ((edited, false)) collectionSize shouldBe 1 - await(repository.collection.find(selector(edited)).toFuture()) shouldBe Some(edited) + await(repository.collection.find(selector(edited)).headOption()) shouldBe Some(edited) } } From 18b5eb47576521dbdf3faa7de2cd9fa4b3f3f2d9 Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Wed, 12 Oct 2022 16:23:52 +0100 Subject: [PATCH 3/7] APID-520 - fixing tests --- .../repository/FieldDefinitionRepository.scala | 1 - .../repository/SubscriptionFieldsRepository.scala | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala index 7ac7eee..77bcb5d 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala @@ -63,7 +63,6 @@ class ApiFieldDefinitionsMongoRepository @Inject() (mongo: MongoComponent) )) with ApiFieldDefinitionsRepository with ApplicationLogger { -// with MongoCrudHelper[ApiFieldDefinitions] { override lazy val collection: MongoCollection[ApiFieldDefinitions] = CollectionFactory diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala index bb1d41b..b3f8f2f 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala @@ -174,14 +174,14 @@ class SubscriptionFieldsMongoRepository @Inject()(mongo: MongoComponent) equal("apiVersion", Codecs.toBson(apiVersion.value))) collection.deleteOne(query) .toFuture() - .map(_.wasAcknowledged()) + .map(_.getDeletedCount > 0) } override def delete(clientId: ClientId): Future[Boolean] = { val query = equal("clientId", Codecs.toBson(clientId.value)) collection.deleteMany(query) .toFuture() - .map(_.wasAcknowledged()) + .map(_.getDeletedCount > 0) } } From 3f154541c20b7b5a6fc9e456a734e8b85306873e Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:24:20 +0100 Subject: [PATCH 4/7] APID-520 - clean up --- .../FieldDefinitionRepository.scala | 2 - .../repository/MongoCrudHelper.scala | 90 ------------------- .../repository/MongoErrorHandler.scala | 67 -------------- .../repository/MongoIndexCreator.scala | 56 ------------ .../SubscriptionFieldsRepository.scala | 34 +------ 5 files changed, 1 insertion(+), 248 deletions(-) delete mode 100644 app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala delete mode 100644 app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala delete mode 100644 app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala index 77bcb5d..2b08f72 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/FieldDefinitionRepository.scala @@ -33,7 +33,6 @@ import uk.gov.hmrc.mongo.play.json.{Codecs, CollectionFactory, PlayMongoReposito import javax.inject.{Inject, Singleton} import scala.concurrent.{ExecutionContext, Future} -import scala.util.control.NonFatal @ImplementedBy(classOf[ApiFieldDefinitionsMongoRepository]) trait ApiFieldDefinitionsRepository { @@ -110,7 +109,6 @@ class ApiFieldDefinitionsMongoRepository @Inject() (mongo: MongoComponent) } override def delete(apiContext: ApiContext, apiVersion: ApiVersion): Future[Boolean] = { - appLogger.info(s"delete apiContext value ${apiContext.value} and apiVersion value ${apiVersion.value}") collection.deleteOne(Filters.and(equal("apiContext", Codecs.toBson(apiContext.value)), equal("apiVersion", Codecs.toBson(apiVersion.value)))) .toFuture() diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala deleted file mode 100644 index 49ea33a..0000000 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoCrudHelper.scala +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2022 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. - */ - -///* -// * Copyright 2022 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.apisubscriptionfields.repository -// -//import org.mongodb.scala.MongoCollection -//import play.api.libs.json._ -// -//import scala.concurrent.ExecutionContext.Implicits.global -//import scala.concurrent.Future -//import uk.gov.hmrc.apisubscriptionfields.model.Types.IsInsert -// -//trait MongoCrudHelper[T] extends MongoIndexCreator with MongoErrorHandler { -// -// protected val collection: MongoCollection[T] -// -// def saveAtomic(selector: JsObject, updateOperations: JsObject)(implicit w: OFormat[T]): Future[(T, IsInsert)] = { -// val updateOp = collection.updateModifier( -// update = updateOperations, -// fetchNewObject = true, -// upsert = true -// ) -// -// collection.findAndModify(selector, updateOp).map { findAndModifyResult => -// val maybeTuple: Option[(T, IsInsert)] = for { -// value <- findAndModifyResult.value -// updateLastError <- findAndModifyResult.lastError -// } yield (value.as[T], !updateLastError.updatedExisting) -// -// maybeTuple.fold[(T, IsInsert)] { -// handleError(selector, findAndModifyResult) -// }(tuple => tuple) -// } -// } -// -// private def handleError(selector: JsObject, findAndModifyResult: collection.BatchCommands.FindAndModifyCommand.FindAndModifyResult) = { -// val error = s"Error upserting database for $selector." -// appLogger.error(s"$error lastError: ${findAndModifyResult.lastError}") -// throw new RuntimeException(error) -// } -// -// def save(entity: T, selector: JsObject)(implicit w: OWrites[T]): Future[(T, IsInsert)] = { -// collection.findOneAndUpdate(ordered=false).one(selector, entity, upsert = true).map { updateWriteResult => (entity, handleSaveError(updateWriteResult, s"Could not save entity: $entity")) } -// } -// -// def getMany(selector: JsObject)(implicit r: Reads[T]): Future[List[T]] = { -// collection.find[JsObject, JsObject](selector, None).cursor[T]().collect[List](Int.MaxValue, Cursor.FailOnError[List[T]]()) -// } -// -// def getOne(selector: JsObject)(implicit r: Reads[T]): Future[Option[T]] = { -// collection.find[JsObject, JsObject](selector, None).one[T] -// } -// -// def deleteOne(selector: JsObject): Future[Boolean] = { -// collection.delete(ordered=false).one(selector, limit = Some(1)).map(handleDeleteError(_, s"Could not delete entity for selector: $selector")) -// } -// -// def deleteMany(selector: JsObject): Future[Boolean] = { -// collection.delete().one(selector).map(handleDeleteError(_, s"Could not delete entity for selector: $selector")) -// } -//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala deleted file mode 100644 index 4d97280..0000000 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandler.scala +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2022 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. - */ - -///* -// * Copyright 2022 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.apisubscriptionfields.repository -// -//import com.mongodb.client.result.DeleteResult -//import uk.gov.hmrc.apisubscriptionfields.model.Types.IsInsert -//import uk.gov.hmrc.apisubscriptionfields.utils.ApplicationLogger -// -//trait MongoErrorHandler extends ApplicationLogger { -// -// def handleDeleteError(result: DeleteResult, exceptionMsg: => String): Boolean = { -// handleError(result, databaseAltered, exceptionMsg) -// } -// -// def handleSaveError(updateWriteResult: UpdateWriteResult, exceptionMsg: => String): IsInsert = { -// -// def handleUpsertError(result: WriteResult) = -// if (databaseAltered(result)) -// updateWriteResult.upserted.nonEmpty -// else -// throw new RuntimeException(exceptionMsg) -// -// handleError(updateWriteResult, handleUpsertError, exceptionMsg) -// } -// -// private def handleError(result: DeleteResult, f: DeleteResult => Boolean, exceptionMsg: String): Boolean = { -// result.writeConcernError.fold(f(result)) { -// errMsg => { -// val errorMsg = s"""$exceptionMsg. $errMsg""" -// appLogger.error(errorMsg) -// throw new RuntimeException(errorMsg) -// } -// } -// } -// -// private def databaseAltered(writeResult: WriteResult): Boolean = writeResult.n > 0 -//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala deleted file mode 100644 index 364154e..0000000 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoIndexCreator.scala +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2022 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. - */ - -///* -// * Copyright 2022 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.apisubscriptionfields.repository -// -//import reactivemongo.api.indexes.IndexType.Ascending -//import reactivemongo.api.indexes.{Index, IndexType} -// -// -//trait MongoIndexCreator { -// -// def createSingleFieldAscendingIndex(indexFieldKey: String, indexName: Option[String], isUnique: Boolean): Index = { -// -// createCompoundIndex(indexFieldMappings = Seq(indexFieldKey -> Ascending), indexName, isUnique) -// } -// -// def createCompoundIndex(indexFieldMappings: Seq[(String, IndexType)], indexName: Option[String], -// isUnique: Boolean, isBackground: Boolean = true): Index = { -// -// Index( -// key = indexFieldMappings, -// name = indexName, -// unique = isUnique, -// background = isBackground -// ) -// } -//} diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala index b3f8f2f..a5f382f 100644 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala +++ b/app/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepository.scala @@ -71,42 +71,10 @@ class SubscriptionFieldsMongoRepository @Inject()(mongo: MongoComponent) .name("fieldsIdIndex") .unique(true)) )) -// { - -// extends ReactiveRepository[SubscriptionFields, BSONObjectID]( -// "subscriptionFields", -// mongoDbProvider.mongo, -// JsonFormatters.SubscriptionFieldsJF, -// ReactiveMongoFormats.objectIdFormats -// ) + with SubscriptionFieldsRepository with ApplicationLogger -// with MongoCrudHelper[SubscriptionFields] with JsonFormatters { -// -// override val mongoCollection: JSONCollection = collection -// -// override def indexes = Seq( -// createCompoundIndex( -// indexFieldMappings = Seq( -// "clientId" -> IndexType.Ascending, -// "apiContext" -> IndexType.Ascending, -// "apiVersion" -> IndexType.Ascending -// ), -// indexName = Some("clientId-apiContext-apiVersion_Index"), -// isUnique = true -// ), -// createSingleFieldAscendingIndex( -// indexFieldKey = "clientId", -// indexName = Some("clientIdIndex"), -// isUnique = false -// ), -// createSingleFieldAscendingIndex( -// indexFieldKey = "fieldsId", -// indexName = Some("fieldsIdIndex"), -// isUnique = true -// ) -// ) override lazy val collection: MongoCollection[SubscriptionFields] = CollectionFactory From 21a36b9016da68257fa517fc39078aeb9b6b3bd3 Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:32:24 +0100 Subject: [PATCH 5/7] APID-520 - clean up --- project/AppDependencies.scala | 9 -- .../repository/MongoErrorHandlerSpec.scala | 124 ------------------ 2 files changed, 133 deletions(-) delete mode 100644 test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala diff --git a/project/AppDependencies.scala b/project/AppDependencies.scala index 3f9735d..39bfa7c 100644 --- a/project/AppDependencies.scala +++ b/project/AppDependencies.scala @@ -21,15 +21,6 @@ object AppDependencies { ) private lazy val testDependencies = Seq( -// "uk.gov.hmrc" %% "bootstrap-test-play-28" % bootstrapVersion, -// "uk.gov.hmrc.mongo" %% "hmrc-mongo-test-play-28" % mongoVersion, -// "org.mockito" %% "mockito-scala-scalatest" % "1.16.46", -// "org.jsoup" % "jsoup" % "1.13.1", -// "org.scalaj" %% "scalaj-http" % "2.4.2", -// "com.github.tomakehurst" % "wiremock-jre8-standalone" % "2.31.0", -// "org.scalacheck" %% "scalacheck" % "1.15.4", -// "org.scalatestplus" %% "scalacheck-1-15" % "3.2.10.0" - "uk.gov.hmrc" %% "bootstrap-test-play-28" % bootstrapVersion, "uk.gov.hmrc.mongo" %% "hmrc-mongo-test-play-28" % "0.68.0", "org.scalatestplus.play" %% "scalatestplus-play" % "3.1.3", diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala deleted file mode 100644 index e61e250..0000000 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/MongoErrorHandlerSpec.scala +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2022 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. - */ - -///* -// * Copyright 2022 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.apisubscriptionfields.repository -// -//import reactivemongo.api.commands._ -//import reactivemongo.bson.BSONInteger -//import uk.gov.hmrc.apisubscriptionfields.AsyncHmrcSpec -// -//class MongoErrorHandlerSpec extends AsyncHmrcSpec { -// -// private val mongoErrorHandler = new MongoErrorHandler {} -// private val BsonValue = BSONInteger(1) -// private val SomeWriteConcernError = Some(WriteConcernError(1, "ERROR")) -// -// "handleDeleteError" should { -// "return true if there are no database errors and at least one record deleted" in { -// val successfulWriteResult = writeResult(alteredRecords = 1) -// -// mongoErrorHandler.handleDeleteError(successfulWriteResult, "ERROR_MSG") shouldBe true -// } -// -// "return false if there are no database errors and no record deleted" in { -// val noDeletedRecordsWriteResult = writeResult(alteredRecords = 0) -// -// mongoErrorHandler.handleDeleteError(noDeletedRecordsWriteResult, "ERROR_MSG") shouldBe false -// } -// -// "throw a RuntimeException if there is a database error" in { -// val writeConcernError = Some(WriteConcernError(1, "ERROR")) -// val errorWriteResult = writeResult(alteredRecords = 0, writeConcernError = writeConcernError) -// -// val caught = intercept[RuntimeException](mongoErrorHandler.handleDeleteError(errorWriteResult, "ERROR_MSG")) -// -// caught.getMessage shouldBe "ERROR_MSG. WriteConcernError(1,ERROR)" -// } -// } -// -// "handleSaveError" should { -// "return true if there are no database errors and at least one record inserted" in { -// val Inserted = Seq(Upserted(0, BsonValue)) -// val successfulInsertWriteResult = updateWriteResult(alteredRecords = 1, upserted = Inserted) -// -// mongoErrorHandler.handleSaveError(successfulInsertWriteResult, "ERROR_MSG") shouldBe true -// } -// -// "return false if there are no database errors and no record deleted and at least one record updated" in { -// val successfulUpdateWriteResult = updateWriteResult(alteredRecords = 1) -// -// mongoErrorHandler.handleSaveError(successfulUpdateWriteResult, "ERROR_MSG") shouldBe false -// } -// -// "throw a RuntimeException if there is a database error" in { -// val errorUpdateWriteResult = updateWriteResult(alteredRecords = 0, writeConcernError = SomeWriteConcernError) -// -// val caught = intercept[RuntimeException](mongoErrorHandler.handleSaveError(errorUpdateWriteResult, "ERROR_MSG")) -// -// caught.getMessage shouldBe "ERROR_MSG. WriteConcernError(1,ERROR)" -// } -// -// "throw a RuntimeException if there are no records altered" in { -// val errorUpdateWriteResult = updateWriteResult(alteredRecords = 0) -// -// val caught = intercept[RuntimeException](mongoErrorHandler.handleSaveError(errorUpdateWriteResult, "ERROR_MSG")) -// -// caught.getMessage shouldBe "ERROR_MSG" -// } -// -// } -// -// private def writeResult(alteredRecords: Int, writeErrors: Seq[WriteError] = Nil, -// writeConcernError: Option[WriteConcernError] = None) = { -// DefaultWriteResult( -// ok = true, -// n = alteredRecords, -// writeErrors = writeErrors, -// writeConcernError = writeConcernError, -// code = None, -// errmsg = None) -// } -// -// private def updateWriteResult(alteredRecords: Int, upserted: Seq[Upserted] = Nil, writeErrors: Seq[WriteError] = Nil, -// writeConcernError: Option[WriteConcernError] = None) = { -// UpdateWriteResult( -// ok = true, -// n = alteredRecords, -// nModified = 0, -// upserted = upserted, -// writeErrors = writeErrors, -// writeConcernError = writeConcernError, -// code = None, -// errmsg = None) -// } -// -//} From cc9bab397818a75a7baf0e3ad862102bf87c9856 Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:34:13 +0100 Subject: [PATCH 6/7] APID-520 - clean up --- .../repository/MongoDb.scala | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala diff --git a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala b/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala deleted file mode 100644 index 2f16b18..0000000 --- a/app/uk/gov/hmrc/apisubscriptionfields/repository/MongoDb.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2022 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. - */ - -///* -// * Copyright 2022 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.apisubscriptionfields.repository -// -//import com.google.inject.ImplementedBy -//import javax.inject.{Inject, Singleton} -// -//@ImplementedBy(classOf[MongoDb]) -//trait MongoDbProvider { -// def mongo: () => DB -//} -// -//@Singleton -//class MongoDb @Inject()(component: ReactiveMongoComponent) extends MongoDbProvider { -// override val mongo: () => DB = component.mongoConnector.db -//} From 0470754278d690df65c6d2a1afd58f642b696a9f Mon Sep 17 00:00:00 2001 From: "siva.isikella" <7499500+sivaprakashiv@users.noreply.github.com> Date: Thu, 13 Oct 2022 10:37:10 +0100 Subject: [PATCH 7/7] APID-520 - clean up --- .../repository/ApiFieldDefinitionsRepositorySpec.scala | 2 -- .../repository/SubscriptionFieldsRepositorySpec.scala | 4 ---- 2 files changed, 6 deletions(-) diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala index f1d5c94..b19c949 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/ApiFieldDefinitionsRepositorySpec.scala @@ -38,8 +38,6 @@ class ApiFieldDefinitionsRepositorySpec extends AnyWordSpec private val repository = app.injector.instanceOf[ApiFieldDefinitionsMongoRepository] -// private val repository = new ApiFieldDefinitionsMongoRepository(mongoDbProvider) - override protected def beforeEach() { super.beforeEach() await(repository.collection.drop.toFuture()) diff --git a/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala b/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala index 3a996b9..ac5d748 100644 --- a/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala +++ b/test/uk/gov/hmrc/apisubscriptionfields/repository/SubscriptionFieldsRepositorySpec.scala @@ -25,7 +25,6 @@ import org.mongodb.scala.model.{Filters, FindOneAndUpdateOptions, ReturnDocument import org.scalatest.matchers.should.Matchers import org.scalatestplus.play.guice.GuiceOneAppPerSuite import play.api.test.{DefaultAwaitTimeout, FutureAwaits} -import uk.gov.hmrc.apisubscriptionfields.SubscriptionFieldsTestData import uk.gov.hmrc.apisubscriptionfields.AsyncHmrcSpec import uk.gov.hmrc.apisubscriptionfields.SubscriptionFieldsTestData.{FakeClientId, FakeClientId2, FakeContext, FakeContext2, FakeVersion, createSubscriptionFieldsWithApiContext, fakeRawContext2, fieldN, uniqueClientId} import uk.gov.hmrc.mongo.play.json.Codecs @@ -41,9 +40,6 @@ class SubscriptionFieldsRepositorySpec extends AsyncHmrcSpec with FutureAwaits with BeforeAndAfterEach { -// private val mongoDbProvider = new MongoDbProvider { -// override val mongo: () => DB = self.mongo -// } private val repository = app.injector.instanceOf[SubscriptionFieldsMongoRepository]