package com.weEat.services import play.api.Configuration import org.mongodb.scala.{MongoClient,MongoDatabase,MongoCollection} import org.mongodb.scala.bson.codecs.Macros._ import org.bson.{BsonReader,BsonWriter} import org.bson.codecs.{Codec,DecoderContext,EncoderContext} import org.bson.codecs.configuration.{CodecProvider,CodecRegistry} import org.bson.codecs.configuration.CodecRegistries.{fromRegistries,fromProviders} import com.weEat.models._ import com.weEat.shared.models.{USDANode,RecipeNode,FoodNode,UnitType} import javax.inject.{Inject,Singleton} import scala.reflect.ClassTag import com.weEat.migrations.{Migration,Metadata} import scala.concurrent.ExecutionContext import scala.util.{Try,Success,Failure} import gov.usda.nal.fdc.models.Nutrient @Singleton class MongoDBService @Inject()(config: Configuration) { implicit val ec = scala.concurrent.ExecutionContext.global private val prefix = "mongo" val url = config.getOptional[String](s"$prefix.url").getOrElse("localhost") val name = config.getOptional[String](s"$prefix.name").getOrElse("recipes") val user = config.getOptional[String](s"$prefix.user").getOrElse("application") val ssl = config.getOptional[Boolean](s"$prefix.ssl").getOrElse(true) val port = config.getOptional[Int](s"$prefix.port").getOrElse(27017) val codecRegistry = fromRegistries( fromProviders(classOf[Metadata]), fromProviders(classOf[FoodNode]), fromProviders(classOf[User]), fromProviders(classOf[Authorization]), fromProviders(classOf[Nutrient]), //fromProviders(classOf[UnitType.Value]), fromProviders(UnitTypeEnumCodecProvider), MongoClient.DEFAULT_CODEC_REGISTRY ) val con = { import org.mongodb.scala._ import org.mongodb.scala.connection.{SslSettings,ClusterSettings} import scala.jdk.CollectionConverters._ implicit val ec: ExecutionContext = ExecutionContext.global val password = Option(config.get[String](s"$prefix.password")) .getOrElse("").toCharArray() val credential = MongoCredential.createCredential(user, name, password) val db = MongoClient( MongoClientSettings.builder() .applyToSslSettings((builder: SslSettings.Builder) => builder.enabled(ssl) ).applyToClusterSettings((builder: ClusterSettings.Builder) => builder.hosts(List(new ServerAddress(url, port)).asJava) ).credential(credential) .build() ).getDatabase(name) .withCodecRegistry(codecRegistry) Migration.updateToLatest(db).map(_ => db) } def apply[T](coll: Collectable[T])(implicit ct: ClassTag[T]) = con.map(_.getCollection[T](coll.collectionName)) def withCollection[T, U](coll: Collectable[U])(fn: (MongoCollection[U] => T)) (implicit ct: ClassTag[U]) = apply(coll).transform({ case Success(collection) => Success(fn(collection)) case Failure(e) => Failure(e) }) } object UnitTypeEnumCodecProvider extends CodecProvider { def isCaseObjectEnum[T](clazz: Class[T]): Boolean = clazz.isInstance(UnitType.MASS) || clazz.isInstance(UnitType.VOLUME) || clazz.isInstance(UnitType.NUMBER) override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] = if (isCaseObjectEnum(clazz)) UnitTypeEnumCodec.asInstanceOf[Codec[T]] else null object UnitTypeEnumCodec extends Codec[UnitType.UnitType] { override def decode( reader: BsonReader, decoderContext: DecoderContext ): UnitType.UnitType = UnitType.withName(reader.readString()) override def encode( writer: BsonWriter, value: UnitType.UnitType, encoderContext: EncoderContext ): Unit = writer.writeString(value.name) override def getEncoderClass: Class[UnitType.UnitType] = UnitType.getClass.asInstanceOf[Class[UnitType.UnitType]] } }