Skip to content

Commit

Permalink
Merge pull request #5 from flowcommerce/form_data_helper
Browse files Browse the repository at this point in the history
Form data helper
  • Loading branch information
Mike Roth committed Jan 18, 2016
2 parents 66c3223 + feea08c commit e8c08c1
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea/
target/
logs/
.ivy2
Expand Down
46 changes: 46 additions & 0 deletions app/io/flow/play/controllers/FlowControllerHelpers.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
package io.flow.play.controllers

import io.flow.play.util.{FormData, Validation}
import play.api.libs.json.JsValue
import play.api.mvc.Results._
import play.api.mvc.{Result, AnyContent}
import scala.concurrent.Future
import play.api.libs.json._
import io.flow.common.v0.models.json._
import scala.concurrent.ExecutionContext.Implicits.global


trait FlowControllerHelpers {

/**
Expand All @@ -19,4 +29,40 @@ trait FlowControllerHelpers {
}
}

def withJsValue(
contentType: Option[String],
body: AnyContent
) (
function: JsValue => Future[Result]
): Future[Result] = {
contentType match {
case Some("application/x-www-form-urlencoded") =>
function(
body.asFormUrlEncoded.map(FormData.formDataToJson(_)).getOrElse(Json.obj())
)
case Some("application/json") =>
function(
body.asJson match {
case Some(json) => json
case None => Json.toJson(Validation.invalidJsonDocument())
}
)
case Some(ct) =>
Future {
UnprocessableEntity(
Json.toJson(
Validation.error(s"Unsupported Content-Type, [$ct]. Must be 'application/x-www-form-urlencoded' or 'application/json'")
)
)
}
case None =>
Future {
UnprocessableEntity(
Json.toJson(
Validation.error(s"Missing Content-Type Header. Must be 'application/x-www-form-urlencoded' or 'application/json'")
)
)
}
}
}
}
42 changes: 42 additions & 0 deletions app/io/flow/play/util/FormData.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package io.flow.play.util

import play.api.libs.json.{JsObject, Json, JsValue}


object FormData {
def formDataToJson(data: Map[String, Seq[String]]): JsValue = {
val nested = data.map{ case (key, value) =>
key.split("\\[").foldRight(
if(key.contains("[]"))
Json.toJson(value) //take seq for arrays
else
Json.toJson(value.headOption.getOrElse(""))
){ case (newKey, v) =>
val newVal = {
val js = (v \ "").getOrElse(v)

//convert '{key: val}' to '[{key: val}]' if previous key specifies array type, otherwise nothing
if(newKey == "]"){
if(!js.toString.startsWith("[")) {
val s = (v \ "").getOrElse(v).toString.
replaceFirst("\\{", "[{").
reverse.
replaceFirst("\\}", "]}").
reverse

Json.toJson(Json.parse(s))
}
else
js
}
else
js
}

Json.obj(newKey.replace("]", "") -> newVal)
}
}

Json.toJson(nested.foldLeft(Json.obj()){ case (a, b) => a.deepMerge(b.as[JsObject]) })
}
}
69 changes: 69 additions & 0 deletions test/io/flow/play/util/FormDataSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.flow.play.util

import org.scalatest.{Matchers, FunSpec}
import play.api.libs.json._


class FormDataSpec extends FunSpec with Matchers {
val fdHelper = FormData

describe("formDataToJson") {

val data: Map[String, Seq[String]] = Map(
"email" -> Seq("[email protected]"),
"name[first]" -> Seq("mike"),
"name[last]" -> Seq("roth"),
"one[two][three][four]" -> Seq("haha"),
"one[two][three][five]" -> Seq("wow"),
"arr[][arr2][]" -> Seq("fruit", "vegitables"),
"tags[]" -> Seq("foo", "bar"),
"yikes" -> Seq("yes", "no"))

it("returns JsValue") {
fdHelper.formDataToJson(data) match {
case res: JsValue => assert(true)
case _ => assert(false)
}
}

it("creates simple json object") {
(fdHelper.formDataToJson(data) \ "email").validate[String] match {
case JsSuccess(succ,_) => succ should be("[email protected]")
case JsError(_) => assert(false)
}
}

it("creates complex json object") {
(fdHelper.formDataToJson(data) \ "name" \ "first").validate[String] match {
case JsSuccess(succ,_) => succ should be("mike")
case JsError(_) => assert(false)
}

(fdHelper.formDataToJson(data) \ "name" \ "last").validate[String] match {
case JsSuccess(succ,_) => succ should be("roth")
case JsError(_) => assert(false)
}
}

it("creates simple array json object") {
(fdHelper.formDataToJson(data) \ "tags").validate[Seq[String]] match {
case JsSuccess(succ,_) => succ should be(Seq("foo", "bar"))
case JsError(_) => assert(false)
}
}

it("creates complex array json object") {
(fdHelper.formDataToJson(data) \ "arr").validate[Seq[JsValue]] match {
case JsSuccess(succ,_) => assert((succ.head \ "arr2").toOption.isDefined)
case JsError(_) => assert(false)
}
}

it("takes first instance of non-array key") {
(fdHelper.formDataToJson(data) \ "yikes").validate[String] match {
case JsSuccess(succ,_) => succ should be("yes")
case JsError(_) => assert(false)
}
}
}
}

0 comments on commit e8c08c1

Please sign in to comment.