| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package com.weEat.services
- import play.api.Configuration
- import org.mongodb.scala.{MongoClient,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._
- import com.weEat.models._
- import com.weEat.shared.models.{FoodNodeId,USDANodeId,Ingredient,UnitType,MeasureUnit,RecipeNodeId}
- import Ingredient._
- import javax.inject.{Inject,Singleton}
- import scala.reflect.ClassTag
- import com.weEat.migrations.{Migration,Metadata}
- import scala.concurrent.ExecutionContext
- import scala.util.{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(
- WrapperCodecProvider(classOf[FoodNodeId],
- { n: USDANodeId =>
- n.copy(nutrients = n.nutrients.map {
- case (k, v) => (k.replace(".", "$"), v)
- })
- }, { n: USDANodeId =>
- n.copy(nutrients = n.nutrients.map {
- case (k, v) => (k.replace("$", "."), v)
- })
- }),
- WrapperCodecProvider(classOf[FoodNodeId],
- { n: RecipeNodeId => n },
- { n: RecipeNodeId => n }
- ),
- classOf[Metadata],
- classOf[FoodNodeId],
- classOf[IngredientId],
- classOf[Ingredient],
- classOf[User],
- classOf[Authorization],
- classOf[Nutrient],
- UnitTypeEnumCodecProvider,
- MeasureUnitCodecProvider
- ),
- 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)
- })
- }
- case class WrapperCodecProvider[T](
- provider: CodecProvider,
- encodeFn: (T => T),
- decodeFn: (T => T)
- )(implicit m: ClassTag[T]) extends CodecProvider {
- override def get[U](c: Class[U], registry: CodecRegistry): Codec[U] =
- if (m.runtimeClass.isAssignableFrom(c) && c.isAssignableFrom(m.runtimeClass))
- WrapperCodec(provider.get(c, registry))(
- encodeFn.asInstanceOf[U => U],
- decodeFn.asInstanceOf[U => U]
- ).asInstanceOf[Codec[U]]
- else null
- }
- case class WrapperCodec[T](val inner: Codec[T])(
- encodeFn: (T => T),
- decodeFn: (T => T)
- ) extends Codec[T] {
- override def decode(
- reader: BsonReader,
- decoderContext: DecoderContext
- ): T = decodeFn(inner.decode(reader, decoderContext))
- override def encode(
- writer: BsonWriter,
- value: T,
- encoderContext: EncoderContext
- ): Unit = inner.encode(writer, encodeFn(value), encoderContext)
- override def getEncoderClass: Class[T] = inner.getEncoderClass
- }
- 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]]
- }
- }
- object MeasureUnitCodecProvider extends CodecProvider {
- def isCaseObjectEnum[T](clazz: Class[T]): Boolean =
- classOf[MeasureUnit].isAssignableFrom(clazz)
- override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] =
- if (isCaseObjectEnum(clazz)) MeasureUnitCodec.asInstanceOf[Codec[T]]
- else null
- object MeasureUnitCodec extends Codec[MeasureUnit] {
- override def decode(
- reader: BsonReader,
- decoderContext: DecoderContext
- ): MeasureUnit = MeasureUnit.fromString(reader.readString()).get
- override def encode(
- writer: BsonWriter,
- value: MeasureUnit,
- encoderContext: EncoderContext
- ): Unit = writer.writeString(value.toString)
- override def getEncoderClass: Class[MeasureUnit] =
- MeasureUnit.getClass.asInstanceOf[Class[MeasureUnit]]
- }
- }
|