<commit_before>package com.xantoria.flippy.api

import scala.concurrent.ExecutionContext
import scala.util.{Success, Failure}
import scala.util.control.NonFatal

import akka.actor.Actor
import net.liftweb.json._
import spray.httpx.LiftJsonSupport
import spray.routing.{ExceptionHandler, HttpService}

import com.xantoria.flippy.condition.Condition
import com.xantoria.flippy.db.Backend
import com.xantoria.flippy.serialization.ContextValue

/**
 * Generic info message about the state of an API request
 */
case class InfoMessage(success: Boolean = true, reason: Option[String] = None)

/**
 * Wrapper for an API "is_active" response
 */
case class IsActive(result: Boolean)

/**
 * Represents a switch definition handled by the API
 */
case class SwitchConfig(name: String, condition: Condition)

object ErrorMessage {
  def apply(t: Throwable): InfoMessage = InfoMessage(
    success = false,
    reason = Some(s"An internal error occurred: ${t.getClass.getName}: ${t.getMessage}")
  )
}

trait APIHandling extends HttpService with LiftJsonSupport {
  protected val backend: Backend
  protected implicit val ec: ExecutionContext

  protected implicit def exceptionHandler = ExceptionHandler {
    case NonFatal(e) => complete(500 -> ErrorMessage(e))
  }

  def handleSwitch = pathPrefix("switch" / Segment) {
    switchName: String => {
      pathEndOrSingleSlash {
        post {
          entity(as[Condition]) {
            cond => onSuccess(backend.configureSwitch(switchName, cond)) {
              _ => complete(200 -> InfoMessage())
            }
          }
        } ~
        get {
          onSuccess(backend.switchConfig(switchName)) {
            cond: Option[Condition] => cond map {
              c: Condition => complete(200 -> c)
            } getOrElse {
              complete(404 -> InfoMessage(success = false, reason = Some("Switch not found")))
            }
          }
        } ~
        put {
          complete(400 -> InfoMessage(success = false, reason = Some("Not implemented!")))
        } ~
        delete {
          onSuccess(backend.deleteSwitch(switchName)) {
            _ => complete(200 -> InfoMessage(success = true))
          }
        }
      } ~
      path("is_active" ~ Slash.?) {
        post {
          entity(as[Map[String, ContextValue]]) {
            data => onSuccess(backend.isActiveSafe(switchName, data mapValues { _.underlying })) {
              b => complete(200 -> IsActive(b))
            }
          }
        }
      }
    }
  }

  def handleSwitchListing = path("switch" ~ Slash.?) {
    get {
      parameters("offset".as[Option[Int]]) {
        offset => onSuccess(backend.listSwitches(offset = offset, limit = Some(10))) {
          data => complete(
            200 -> data.map { s => SwitchConfig(s._1, s._2) }
          )
        }
      }
    }
  }

  def flippyRoutes = handleSwitch ~ handleSwitchListing
}<commit_msg>No success = true as it's default<commit_after>package com.xantoria.flippy.api

import scala.concurrent.ExecutionContext
import scala.util.{Success, Failure}
import scala.util.control.NonFatal

import akka.actor.Actor
import net.liftweb.json._
import spray.httpx.LiftJsonSupport
import spray.routing.{ExceptionHandler, HttpService}

import com.xantoria.flippy.condition.Condition
import com.xantoria.flippy.db.Backend
import com.xantoria.flippy.serialization.ContextValue

/**
 * Generic info message about the state of an API request
 */
case class InfoMessage(success: Boolean = true, reason: Option[String] = None)

/**
 * Wrapper for an API "is_active" response
 */
case class IsActive(result: Boolean)

/**
 * Represents a switch definition handled by the API
 */
case class SwitchConfig(name: String, condition: Condition)

object ErrorMessage {
  def apply(t: Throwable): InfoMessage = InfoMessage(
    success = false,
    reason = Some(s"An internal error occurred: ${t.getClass.getName}: ${t.getMessage}")
  )
}

trait APIHandling extends HttpService with LiftJsonSupport {
  protected val backend: Backend
  protected implicit val ec: ExecutionContext

  protected implicit def exceptionHandler = ExceptionHandler {
    case NonFatal(e) => complete(500 -> ErrorMessage(e))
  }

  def handleSwitch = pathPrefix("switch" / Segment) {
    switchName: String => {
      pathEndOrSingleSlash {
        post {
          entity(as[Condition]) {
            cond => onSuccess(backend.configureSwitch(switchName, cond)) {
              _ => complete(200 -> InfoMessage())
            }
          }
        } ~
        get {
          onSuccess(backend.switchConfig(switchName)) {
            cond: Option[Condition] => cond map {
              c: Condition => complete(200 -> c)
            } getOrElse {
              complete(404 -> InfoMessage(success = false, reason = Some("Switch not found")))
            }
          }
        } ~
        put {
          complete(400 -> InfoMessage(success = false, reason = Some("Not implemented!")))
        } ~
        delete {
          onSuccess(backend.deleteSwitch(switchName)) {
            _ => complete(200 -> InfoMessage())
          }
        }
      } ~
      path("is_active" ~ Slash.?) {
        post {
          entity(as[Map[String, ContextValue]]) {
            data => onSuccess(backend.isActiveSafe(switchName, data mapValues { _.underlying })) {
              b => complete(200 -> IsActive(b))
            }
          }
        }
      }
    }
  }

  def handleSwitchListing = path("switch" ~ Slash.?) {
    get {
      parameters("offset".as[Option[Int]]) {
        offset => onSuccess(backend.listSwitches(offset = offset, limit = Some(10))) {
          data => complete(
            200 -> data.map { s => SwitchConfig(s._1, s._2) }
          )
        }
      }
    }
  }

  def flippyRoutes = handleSwitch ~ handleSwitchListing
}
{'fraction_non_alphanumeric': 0.11554404145077721, 'fraction_numerical': 0.009671848013816925, 'mean_word_length': 2.486453943407586, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6432597', 'n_tokens_mistral': 1671, 'n_tokens_neox': 1634, 'n_words': 374}