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

io.atlassian.aws.sqs.MessageAttributeDecoder.scala Maven / Gradle / Ivy

package io.atlassian.aws
package sqs

import com.amazonaws.services.sqs.model.MessageAttributeValue
import org.joda.time.{ DateTime, DateTimeZone }

import scalaz.Monad
import scalaz.syntax.id._
import scalaz.syntax.bind._

/**
 * Represents a function that tries to convert an AttributeValue into a
 * Scala value (typically that represents a field in an object).
 */
case class MessageAttributeDecoder[A] private[MessageAttributeDecoder] (run: Value => Attempt[A]) extends (Value => Attempt[A]) {
  def apply(o: Value): Attempt[A] =
    run(o)

  def map[B](f: A => B): MessageAttributeDecoder[B] = flatMap(f andThen MessageAttributeDecoder.ok)

  def flatMap[B](f: A => MessageAttributeDecoder[B]): MessageAttributeDecoder[B] =
    MessageAttributeDecoder { m => run(m) >>= { f(_)(m) } }
}

/**
 * Contains implicit decoders for different types and useful functions for creating your own [[MessageAttributeDecoder]]s.
 */
object MessageAttributeDecoder {
  def apply[A: MessageAttributeDecoder] =
    implicitly[MessageAttributeDecoder[A]]

  private def option[A](f: PartialFunction[Value, Attempt[A]]) =
    MessageAttributeDecoder[A](f)

  def ok[A](strict: A): MessageAttributeDecoder[A] = from { Attempt.ok(strict) }

  def from[A](a: Attempt[A]): MessageAttributeDecoder[A] = MessageAttributeDecoder { _ => a }

  def mandatory[A](typeLabel: String)(f: MessageAttributeValue => Attempt[A]): MessageAttributeDecoder[A] =
    option {
      case None     => Attempt.fail(s"Could not decode $typeLabel value")
      case Some(av) => f(av)
    }

  @deprecated("use mandatory instead", "1.0")
  def mandatoryField[A](f: MessageAttributeValue => A, typeLabel: String): MessageAttributeDecoder[A] =
    mandatory(typeLabel) { v => Attempt.safe { f(v) } }

  protected def mandatoryFromString[A](typeLabel: String)(convert: String => A) =
    mandatory(typeLabel) { v => Attempt.safe { convert(v.getStringValue) } }

  implicit val LongMessageAttributeDecode: MessageAttributeDecoder[Long] =
    mandatoryFromString("Long") { _.toLong }

  implicit val IntMessageAttributeDecode: MessageAttributeDecoder[Int] =
    mandatoryFromString("Int") { _.toInt }

  implicit val DateTimeMessageAttributeDecode: MessageAttributeDecoder[DateTime] =
    mandatoryFromString("DateTime") { _.toLong |> { i => new DateTime(i, DateTimeZone.UTC) } }

  implicit val StringMessageAttributeDecode: MessageAttributeDecoder[String] =
    option {
      // No attribute value means an empty string (because DynamoDB doesn't support empty strings as attribute values)
      case None     => Attempt.ok("")
      case Some(av) => Option(av.getStringValue).map { Attempt.ok }.getOrElse { Attempt.fail("No string value present") }
    }

  implicit def OptionMessageAttributeDecode[A](implicit decode: MessageAttributeDecoder[A]): MessageAttributeDecoder[Option[A]] =
    option {
      case None                => Attempt.ok(None)
      case someValue @ Some(_) => decode(someValue).toOption |> Attempt.ok
    }

  implicit val DecodeMessageAttributeValueMonad: Monad[MessageAttributeDecoder] =
    new Monad[MessageAttributeDecoder] {
      def point[A](v: => A) = ok(v)
      def bind[A, B](m: MessageAttributeDecoder[A])(f: A => MessageAttributeDecoder[B]) = m flatMap f
      override def map[A, B](m: MessageAttributeDecoder[A])(f: A => B) = m map f
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy