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

feature4s.aerospike.AerospikeFeatureRegistry.scala Maven / Gradle / Ivy

package feature4s.aerospike

import com.aerospike.client.{AerospikeClient, AerospikeException, Bin, ResultCode}
import com.aerospike.client.policy.{RecordExistsAction, WritePolicy}
import com.aerospike.client.query.Statement
import feature4s.compat.CollectionConverters._
import feature4s.{Feature, FeatureNotFound, FeatureRegistry, FeatureState}
import feature4s.monad.MonadError
import feature4s.monad.syntax._

abstract class AerospikeFeatureRegistry[F[_]](
  client: AerospikeClient,
  namespace: String,
  implicit val monad: MonadError[F]
) extends FeatureRegistry[F] {
  private val writePolicy = new WritePolicy(client.writePolicyDefault)
  writePolicy.expiration = -1 // never expire

  private val notOverride = new WritePolicy(client.writePolicyDefault)
  notOverride.recordExistsAction = RecordExistsAction.CREATE_ONLY

  override def register(
    featureName: String,
    enable: Boolean,
    description: Option[String]
  ): F[Feature[F]] =
    monad
      .handleErrorWith(
        monad.eval(
          client.put(
            notOverride,
            key(featureName, namespace),
            new Bin(ValueFieldName, enable)
          )
        )
      ) {
        case err: AerospikeException if err.getResultCode == ResultCode.KEY_EXISTS_ERROR =>
          monad.unit
      }
      .flatMap(_ => updateInfo(featureName, description.getOrElse("")))
      .map(_ => Feature(featureName, () => valueAccessor(featureName), description))

  private def valueAccessor(featureName: String): F[Boolean] =
    monad
      .eval(client.get(client.readPolicyDefault, key(featureName, namespace)))
      .flatMap(record =>
        monad.ifM(monad.pure(record != null))(
          ifTrue = monad.pure(record.getBoolean(ValueFieldName)),
          ifFalse = monad.raiseError(FeatureNotFound(featureName))
        )
      )

  private def updateInfo(featureName: String, description: String): F[Unit] =
    monad
      .eval(
        client.put(
          writePolicy,
          key(featureName, namespace),
          new Bin(FeatureNameFieldName, featureName),
          new Bin(DescriptionFieldName, description)
        )
      )
      .void

  override def recreate(
    featureName: String,
    enable: Boolean,
    description: Option[String]
  ): F[Feature[F]] =
    monad
      .eval(
        client.put(
          writePolicy,
          key(featureName, namespace),
          new Bin(FeatureNameFieldName, featureName),
          new Bin(ValueFieldName, enable),
          new Bin(DescriptionFieldName, description.getOrElse(""))
        )
      )
      .map(_ => Feature(featureName, () => valueAccessor(featureName), description))

  override def update(featureName: String, enable: Boolean): F[Unit] =
    monad.ifM(isExist(featureName))(
      ifTrue = monad.eval(
        client.put(writePolicy, key(featureName, namespace), new Bin(ValueFieldName, enable))
      ),
      ifFalse = monad.raiseError(FeatureNotFound(featureName))
    )

  override def featureList(): F[List[FeatureState]] = {
    val statement = new Statement()
    statement.setBinNames(FeatureNameFieldName, ValueFieldName, DescriptionFieldName)
    statement.setNamespace(namespace)
    statement.setSetName(DefaultSetName)

    monad
      .eval(client.query(client.queryPolicyDefault, statement))
      .map(set => set.iterator())
      .map(records =>
        records
          .map(r =>
            FeatureState(
              name = Option(r.record.getString(FeatureNameFieldName))
                .filter(_.nonEmpty)
                .getOrElse("empty_feature_name"),
              isEnable = r.record.getBoolean(ValueFieldName),
              description = Option(r.record.getString(DescriptionFieldName)).filter(_.nonEmpty)
            )
          )
          .toList
      )
  }

  override def isExist(featureName: String): F[Boolean] =
    monad.eval(client.exists(writePolicy, key(featureName, namespace)))

  override def remove(featureName: String): F[Boolean] =
    monad.eval(client.delete(writePolicy, key(featureName, namespace)))

  override def close(): F[Unit] = monad.unit

  override def monadError: MonadError[F] = monad
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy