SkillAgentSearch skills...

Jsentric

Json contract patterns, validation, lenses and query

Install / Use

/learn @HigherState/Jsentric
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

jsentric

Json contract patterns, validation, lenses and query

"io.higherstate" %% "jsentric" % "1.0.0"

resolvers ++= Seq(
  "Sonatype releases" at "http://oss.sonatype.org/content/repositories/releases/",
)

jsentric is built upon argonaut and is designed to facilitate the use of the basic json datatypes in cases where we have partially dynamic data or are regularly moving through bounded context and may not wish to constantly serialize/deserialize from class objects.

jsentric works by describing a singleton contract which represents data we might wish to extract from the json data structure. By doing so, we get easy validation, lenses and even a type safe mongo db query generator.

    /*define a contract,
    \  \?  \! expected, optional, default properties
    \: \:?  \:!  expected, optional, default array properties
    \\ \\? expected, option object properties
   */
  object Order extends Contract {
    val firstName = \[String]("firstName", nonEmptyOrWhiteSpace)
    val lastName = \[String]("lastName", nonEmptyOrWhiteSpace)
    val orderId = \?[Int]("orderId", reserved && immutable)

    val email = new \\("email") {
      val friendlyName = \?[String]("friendlyName")
      val address = \[String]("address")
    }
    val status = \?[String]("status", in("pending", "processing", "sent") && reserved)
    val notes = \?[String]("notes", internal)

    val orderLines = \:[(String, Int)]("orderLines", forall(custom[(String, Int)](ol => ol._2 >= 0, "Cannot order negative items")))

    import Composite._
    //Combine properties to make a composite pattern matcher
    lazy val fullName = firstName @: lastName
  }

  import argonaut._

  //Create a new Json object
  val newOrder = Order.$create{o =>
    o.firstName.$set("John") ~
    o.lastName.$set("Smith") ~
    o.email.address.$set("johnSmith@test.com") ~
    o.orderLines.$append("Socks" -> 3)
  }

  //validate a new json object
  val validated = Order.$validate(newOrder)

  //pattern match property values
  newOrder match {
    case Order.email.address(email) && Order.email.friendlyName(Some(name)) =>
      println(s"$email <$name>")
    case Order.email.address(email) && Order.fullName(firstName, lastName) =>
      println(s"$email <$firstName $lastName>")
  }

  //make changes to the json object.
  val pending =
    Order{o =>
      o.orderId.$set(123) ~
      o.status.$set("pending") ~
      o.notes.$modify(maybe => Some(maybe.foldLeft("Order now pending.")(_ + _)))
    }(newOrder)

  //strip out any properties marked internal
  val sendToClient = Order.$sanitize(pending)

  //generate query json
  val relatedOrdersQuery = Order.orderId.$gt(56) && Order.status.$in("processing", "sent")
  //experimental convert to postgres jsonb clause
  val postgresQuery = QueryJsonb("data", relatedOrdersQuery)

  import scalaz.{\/, \/-}
  //create a dynamic property
  val dynamic = Order.$dynamic[\/[String, Int]]("age")

  sendToClient match {
    case dynamic(Some(\/-(ageInt))) =>
      println(ageInt)
    case _ =>
  }

  val statusDelta = Order.$create(_.status.$set("processing"))
  //validate against current state
  Order.$validate(statusDelta, pending)
  //apply delta to current state
  val processing = pending.delta(statusDelta)

  //Define subcontract for reusable or recursive structures
  trait UserTimestamp extends SubContract {
    val account = \[String]("account")
    val timestamp = \[Long]("timestamp")
  }
  object Element extends Contract {
    val created = new \\("created", immutable) with UserTimestamp
    val modified = new \\("modified") with UserTimestamp
  }

  //try to force a match even if wrong type
  import LooseCodecs._
  Json("orderId" := "23628") match {
    case Order.orderId(Some(id)) => id
  }

*Auto generation of schema information is still a work in progress

*mongo query is not a full feature set.

Related Skills

View on GitHub
GitHub Stars15
CategoryDevelopment
Updated9y ago
Forks0

Languages

Scala

Security Score

75/100

Audited on Jul 17, 2016

No findings