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

org.scassandra.server.priming.routes.PrimingJsonHelper.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2016 Christopher Batey and Dogan Narinc
 *
 * 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 org.scassandra.server.priming.routes

import java.util.concurrent.TimeUnit

import com.typesafe.scalalogging.LazyLogging
import org.scassandra.codec.datatype.DataType
import org.scassandra.codec.messages.{ColumnSpecWithoutTable, Row, RowMetadata}
import org.scassandra.codec.{Consistency, Rows}
import org.scassandra.server.priming._
import org.scassandra.server.priming.json._
import org.scassandra.server.priming.query._
import org.scassandra.{codec => c}
import scodec.bits.ByteVector

import scala.concurrent.duration.FiniteDuration
import scala.util.{Failure, Try, Success => TSuccess}

object PrimingJsonHelper extends LazyLogging {

  def extractPrimeCriteria(primeQueryRequest: PrimeQuerySingle): Try[PrimeCriteria] = {
    val primeConsistencies = primeQueryRequest.when.consistency.getOrElse(Consistency.all)

    primeQueryRequest.when match {
      // Prime for a specific query
      case When(Some(query), None, _, _, _) =>
        TSuccess(PrimeCriteria(query, primeConsistencies, patternMatch = false))
      // Prime for a query pattern
      case When(None, Some(queryPattern), _, _, _) => TSuccess(PrimeCriteria(queryPattern, primeConsistencies, patternMatch = true))
      case _ => Failure(new IllegalArgumentException("Can't specify query and queryPattern"))
    }
  }

  def extractPrime(thenDo: ThenProvider, keyspace: Option[String] = Some(""), table: Option[String] = Some("")) = {
    val fixedDelay = thenDo.fixedDelay.map(FiniteDuration(_, TimeUnit.MILLISECONDS))
    val config = thenDo.config.getOrElse(Map.empty)

    // For errors expecting a consistency level, only provide one if one is present in the prime, otherwise
    // set it explicitly to null.  This is a bit fragile, but it allows us to detect whether or not we are allowed
    // to override the error message with the statement used in the query.  Could have used [Option] but in this
    // case we want to make it required on the Error messages.
    thenDo.result.getOrElse(Success) match {
      // Special case ClosedConnectionReport as it produces a fatal error.
      case ClosedConnection => ClosedConnectionReport(config.getOrElse(ErrorConstants.CloseType, "close"), fixedDelay)
      case report =>
        val message = report match {
          case Success => extractRows(thenDo, keyspace, table)
          case ServerError => c.ServerError(config.getOrElse(ErrorConstants.Message, "Server Error"))
          case ProtocolError => c.ProtocolError(config.getOrElse(ErrorConstants.Message, "Protocol Error"))
          case BadCredentials => c.AuthenticationError(config.getOrElse(ErrorConstants.Message, "Bad Credentials"))
          case Unavailable => c.Unavailable(
            config.getOrElse(ErrorConstants.Message, "Unavailable Exception"),
            config.get(ErrorConstants.ConsistencyLevel).map(Consistency.withName).orNull,
            config.getOrElse(ErrorConstants.RequiredResponse, "1").toInt,
            config.getOrElse(ErrorConstants.Alive, "0").toInt
          )
          case Overloaded => c.Overloaded(config.getOrElse(ErrorConstants.Message, "Overloaded"))
          case IsBootstrapping => c.IsBootstrapping(config.getOrElse(ErrorConstants.Message, "Bootstrapping"))
          case TruncateError => c.TruncateError(config.getOrElse(ErrorConstants.Message, "Truncate Error"))
          case WriteTimeout => c.WriteTimeout(
            config.getOrElse(ErrorConstants.Message, "Write Request Timeout"),
            config.get(ErrorConstants.ConsistencyLevel).map(Consistency.withName).orNull,
            config.getOrElse(ErrorConstants.ReceivedResponse, "0").toInt,
            config.getOrElse(ErrorConstants.RequiredResponse, "1").toInt,
            config.getOrElse(ErrorConstants.WriteType, "SIMPLE")
          )
          case ReadTimeout => c.ReadTimeout(
            config.getOrElse(ErrorConstants.Message, "Read Request Timeout"),
            config.get(ErrorConstants.ConsistencyLevel).map(Consistency.withName).orNull,
            config.getOrElse(ErrorConstants.ReceivedResponse, "0").toInt,
            config.getOrElse(ErrorConstants.RequiredResponse, "1").toInt,
            config.getOrElse(ErrorConstants.DataPresent, "false").toBoolean
          )
          case ReadFailure => c.ReadFailure(
            config.getOrElse(ErrorConstants.Message, "Read Failure"),
            config.get(ErrorConstants.ConsistencyLevel).map(Consistency.withName).orNull,
            config.getOrElse(ErrorConstants.ReceivedResponse, "0").toInt,
            config.getOrElse(ErrorConstants.RequiredResponse, "1").toInt,
            config.getOrElse(ErrorConstants.NumberOfFailures, "1").toInt,
            config.getOrElse(ErrorConstants.DataPresent, "false").toBoolean
          )
          case WriteFailure => c.WriteFailure(
            config.getOrElse(ErrorConstants.Message, "Write Failure"),
            config.get(ErrorConstants.ConsistencyLevel).map(Consistency.withName).orNull,
            config.getOrElse(ErrorConstants.ReceivedResponse, "0").toInt,
            config.getOrElse(ErrorConstants.RequiredResponse, "1").toInt,
            config.getOrElse(ErrorConstants.NumberOfFailures, "1").toInt,
            config.getOrElse(ErrorConstants.WriteType, "SIMPLE")
          )
          case FunctionFailure => c.FunctionFailure(
            config.getOrElse(ErrorConstants.Message, "Function Failure"),
            config.getOrElse(ErrorConstants.Keyspace, "keyspace"),
            config.getOrElse(ErrorConstants.Function, "function"),
            config.getOrElse(ErrorConstants.Argtypes, "argtypes").split(",").toList
          )
          case SyntaxError => c.SyntaxError(config.getOrElse(ErrorConstants.Message, "Syntax Error"))
          case Unauthorized => c.Unauthorized(config.getOrElse(ErrorConstants.Message, "Unauthorized"))
          case Invalid => c.Invalid(config.getOrElse(ErrorConstants.Message, "Invalid"))
          case ConfigError => c.ConfigError(config.getOrElse(ErrorConstants.Message, "Config Error"))
          case AlreadyExists => c.AlreadyExists(
            config.getOrElse(ErrorConstants.Message, "Already Exists"),
            config.getOrElse(ErrorConstants.Keyspace, "keyspace"),
            config.getOrElse(ErrorConstants.Table, "")
          )
          case Unprepared => c.Unprepared(
            config.getOrElse(ErrorConstants.Message, "Unprepared"),
            ByteVector.fromValidHex(config.getOrElse(ErrorConstants.PrepareId, "0x").toLowerCase)
          )
        }

        // In the case of a Then implementation, provide the variable types.
        thenDo match {
          case t: Then =>
            Reply(message, fixedDelay, variableTypes = t.variable_types)
          case _ =>
            Reply(message, fixedDelay)
        }
    }
  }

  private def extractRows(thenDo: ThenProvider, keyspace: Option[String] = Some(""), table: Option[String] = Some("")): Rows = {
    // Convert Map[String,Any] -> Row
    val cRows: List[Row] = thenDo.rows.getOrElse(Nil).map(columns => Row(columns))

    // column types from prime.
    val colTypes = thenDo.column_types.getOrElse(Map())
    // extract all column names not in column types
    val nonPresentColumns: Map[String, DataType] = cRows
      .flatMap(row => row.columns)
      .map(_._1) // extract column names from columnName/dataType pairing.
      .filter(name => !colTypes.contains(name)) // remove all elements not in columnTypes.
      .toSet // remove duplicates.
      .map((name: String) => (name, DataType.Varchar)) // create the default Varchar mapping.
      .toMap

    // combine the column types from the prime and the default column types and map to a ColumnSpec.
    val columnSpec = (colTypes ++ nonPresentColumns).map {
      case ((name, dataType)) => ColumnSpecWithoutTable(name, dataType)
    }.toList

    val metadata = RowMetadata(
      keyspace = keyspace.orElse(Some("")),
      table = table.orElse(Some("")),
      columnSpec = Some(columnSpec)
    )

    Rows(metadata, cRows)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy