All Downloads are FREE. Search and download functionalities are using the official Maven repository.

mongo4cats.embedded.EmbeddedMongo.scala Maven / Gradle / Ivy

/*
 * Copyright 2020 Kirill5k
 *
 * 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 mongo4cats.embedded

import cats.effect.{Async, Resource}
import cats.syntax.functor._
import com.mongodb.client.MongoClients
import de.flapdoodle.embed.mongo.commands.MongodArguments
import de.flapdoodle.embed.mongo.config.Net
import de.flapdoodle.embed.mongo.distribution.Version
import de.flapdoodle.embed.mongo.transitions.{Mongod, RunningMongodProcess}
import de.flapdoodle.reverse.transitions.Start
import de.flapdoodle.reverse.{Listener, StateID, TransitionWalker}
import org.bson.Document

trait EmbeddedMongo {
  protected val mongoVersion: Version         = Version.V7_0_0
  protected val mongoPort: Int                = 27017
  protected val mongoUsername: Option[String] = None
  protected val mongoPassword: Option[String] = None

  def withRunningEmbeddedMongo[F[_]: Async, A](test: => F[A]): F[A] =
    EmbeddedMongo.start[F](mongoPort, mongoUsername, mongoPassword, mongoVersion).use(_ => test)

  def withRunningEmbeddedMongo[F[_]: Async, A](
      mongoUsername: String,
      mongoPassword: String
  )(
      test: => F[A]
  ): F[A] =
    EmbeddedMongo.start[F](mongoPort, Some(mongoUsername), Some(mongoPassword), mongoVersion).use(_ => test)

  def withRunningEmbeddedMongo[F[_]: Async, A](
      mongoPort: Int
  )(
      test: => F[A]
  ): F[A] =
    EmbeddedMongo.start[F](mongoPort, mongoUsername, mongoPassword, mongoVersion).use(_ => test)

  def withRunningEmbeddedMongo[F[_]: Async, A](
      mongoPort: Int,
      mongoUsername: String,
      mongoPassword: String
  )(
      test: => F[A]
  ): F[A] =
    EmbeddedMongo.start[F](mongoPort, Some(mongoUsername), Some(mongoPassword), mongoVersion).use(_ => test)
}

object EmbeddedMongo {

  def start[F[_]](
      port: Int,
      username: Option[String],
      password: Option[String],
      version: Version,
      remainingAttempts: Int = 10
  )(implicit F: Async[F]): Resource[F, Unit] =
    Resource
      .fromAutoCloseable(F.delay(startMongod(port, username, password, version)))
      .void
      .handleErrorWith[Unit, Throwable] { error =>
        if (remainingAttempts <= 0) Resource.raiseError(error)
        else start[F](port, username, password, version, remainingAttempts - 1)
      }

  private def startMongod(
      port: Int,
      username: Option[String],
      password: Option[String],
      version: Version
  ): TransitionWalker.ReachedState[RunningMongodProcess] = {
    val withAuth = username.isDefined && password.isDefined
    val listener = if (withAuth) Some(insertUserListener(username.get, password.get)) else None
    Mongod
      .builder()
      .net(Start.to(classOf[Net]).initializedWith(Net.defaults().withPort(port)))
      .mongodArguments(Start.to(classOf[MongodArguments]).initializedWith(MongodArguments.defaults().withAuth(withAuth)))
      .build()
      .start(version, listener.toList: _*)
  }

  private def insertUserListener(username: String, password: String): Listener =
    Listener
      .typedBuilder()
      .onStateReached[RunningMongodProcess](
        StateID.of(classOf[RunningMongodProcess]),
        { runningProcess =>
          val createUser = new Document("createUser", username)
            .append("pwd", password)
            .append("roles", java.util.Arrays.asList("userAdminAnyDatabase", "dbAdminAnyDatabase", "readWriteAnyDatabase"))

          val address = runningProcess.getServerAddress
          val client  = MongoClients.create(s"mongodb://${address.getHost}:${address.getPort}")
          try {
            val db = client.getDatabase("admin")
            db.runCommand(createUser)
            ()
          } finally client.close()
        }
      )
      .build()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy