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

io.cardell.openfeature.provider.java.JavaConverters.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2023 Alex Cardell
 *
 * 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 io.cardell.openfeature.provider.java

import dev.openfeature.sdk.ErrorCode.FLAG_NOT_FOUND
import dev.openfeature.sdk.ErrorCode.GENERAL
import dev.openfeature.sdk.ErrorCode.INVALID_CONTEXT
import dev.openfeature.sdk.ErrorCode.PARSE_ERROR
import dev.openfeature.sdk.ErrorCode.PROVIDER_NOT_READY
import dev.openfeature.sdk.ErrorCode.TARGETING_KEY_MISSING
import dev.openfeature.sdk.ErrorCode.TYPE_MISMATCH
import dev.openfeature.sdk.Reason.CACHED
import dev.openfeature.sdk.Reason.DEFAULT
import dev.openfeature.sdk.Reason.DISABLED
import dev.openfeature.sdk.Reason.ERROR
import dev.openfeature.sdk.Reason.SPLIT
import dev.openfeature.sdk.Reason.STATIC
import dev.openfeature.sdk.Reason.TARGETING_MATCH
import dev.openfeature.sdk.Reason.UNKNOWN
import dev.openfeature.sdk.{ErrorCode => JErrorCode}
import dev.openfeature.sdk.{ImmutableContext => JContext}
import dev.openfeature.sdk.{ImmutableStructure => JStructure}
import dev.openfeature.sdk.{ProviderEvaluation => JEvaluation}
import dev.openfeature.sdk.{Reason => JReason}
import dev.openfeature.sdk.{Value => JValue}
import scala.jdk.CollectionConverters._
import scala.util.Failure
import scala.util.Success
import scala.util.Try

import io.cardell.openfeature.ContextValue
import io.cardell.openfeature.ContextValue.BooleanValue
import io.cardell.openfeature.ContextValue.DoubleValue
import io.cardell.openfeature.ContextValue.IntValue
import io.cardell.openfeature.ContextValue.StringValue
import io.cardell.openfeature.ErrorCode
import io.cardell.openfeature.EvaluationContext
import io.cardell.openfeature.EvaluationReason
import io.cardell.openfeature.FlagValue
import io.cardell.openfeature.Structure
import io.cardell.openfeature.provider.ResolutionDetails

object ToJavaConverters {

  def contextValue(value: ContextValue): JValue =
    value match {
      case BooleanValue(b) => new JValue(b)
      case DoubleValue(d)  => new JValue(d)
      case IntValue(i)     => new JValue(i)
      case StringValue(s)  => new JValue(s)
    }

  def evaluationContext(ec: EvaluationContext): JContext = {
    val values = ec.values.map { case (k, v) => (k, contextValue(v)) }.asJava
    new JContext(values)
  }

  def structure(structure: Structure): JStructure = {
    val values =
      structure.values.map { case (k, v) => (k, flagValue(v)) }.asJava

    new JStructure(values)
  }

  def flagValue(value: FlagValue): JValue =
    value match {
      case FlagValue.BooleanValue(b)   => new JValue(b)
      case FlagValue.IntValue(i)       => new JValue(i)
      case FlagValue.DoubleValue(d)    => new JValue(d)
      case FlagValue.StringValue(s)    => new JValue(s)
      case FlagValue.StructureValue(s) => new JValue(structure(s))
    }

}

object FromJavaConverters {

  def errorCode(ec: JErrorCode): ErrorCode =
    ec match {
      case GENERAL               => ErrorCode.General
      case PARSE_ERROR           => ErrorCode.ParseError
      case TYPE_MISMATCH         => ErrorCode.TypeMismatch
      case TARGETING_KEY_MISSING => ErrorCode.TargetingKeyMissing
      case PROVIDER_NOT_READY    => ErrorCode.ProviderNotReady
      case INVALID_CONTEXT       => ErrorCode.InvalidContext
      case FLAG_NOT_FOUND        => ErrorCode.FlagNotFound
    }

  def reason(reason: JReason): EvaluationReason =
    reason match {
      case DEFAULT         => EvaluationReason.Default
      case TARGETING_MATCH => EvaluationReason.TargetingMatch
      case DISABLED        => EvaluationReason.Disabled
      case UNKNOWN         => EvaluationReason.Unknown
      case ERROR           => EvaluationReason.Error
      case SPLIT           => EvaluationReason.Split
      case CACHED          => EvaluationReason.Cached
      case STATIC          => EvaluationReason.Static
    }

  def evaluation[A, B](ev: JEvaluation[A])(
      f: A => B
  ): ResolutionDetails[B] = {
    val value     = ev.getValue()
    val converted = f(value)

    ResolutionDetails[B](
      value = converted,
      reason = Option(ev.getReason())
        .map(JReason.valueOf)
        .map(FromJavaConverters.reason),
      errorCode = Option(ev.getErrorCode())
        .map(FromJavaConverters.errorCode),
      errorMessage = Option(ev.getErrorMessage()),
      variant = Option(ev.getVariant()),
      metadata = None
    )
  }

  def evaluation[A](ev: JEvaluation[A]): ResolutionDetails[A] =
    evaluation[A, A](ev)(identity[A] _)

  def structure(structure: JStructure): Structure = {
    val values = Map.from(
      structure
        .asMap()
        .asScala
        .map { case (k, v) => (k, value(v)) }
    )

    Structure(values)
  }

  def value(value: JValue): FlagValue =
    value match {
      case v if v.isBoolean() => FlagValue.BooleanValue(v.asBoolean())
      case v if v.isNumber() =>
        Try(v.asInteger()) match {
          case Success(i) => FlagValue.IntValue(i)
          case Failure(_) => FlagValue.DoubleValue(v.asDouble())
        }
      case v if v.isString() => FlagValue.StringValue(v.asString())
      case v if v.isStructure() =>
        FlagValue.StructureValue(
          structure(new JStructure(v.asStructure().asMap()))
        )
      case _ => ??? // TODO handle
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy