Skip to content

Commit

Permalink
Merge pull request #1188 from jrudolph/jr/w/541-static-formField-unma…
Browse files Browse the repository at this point in the history
…rshalling

Simplify formField implicits a bit #541
  • Loading branch information
jrudolph committed Oct 24, 2017
2 parents 3489473 + e23014a commit f5f49c4
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ object StrictForm {
def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer) = fsu(value)
def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer) = feu(value.entity)
}

implicit val stringFieldUnmarshaller: FieldUnmarshaller[String] =
new FieldUnmarshaller[String] {
def unmarshalString(value: String)(implicit ec: ExecutionContext, mat: Materializer): Future[String] = FastFuture.successful(value)
def unmarshalPart(value: Multipart.FormData.BodyPart.Strict)(implicit ec: ExecutionContext, mat: Materializer): Future[String] =
Unmarshaller.stringUnmarshaller(value.entity)
}
}
sealed abstract class LowPrioImplicits {
implicit def fromFSU[T](implicit fsu: FromStringUnmarshaller[T]): FieldUnmarshaller[T] =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package akka.http.scaladsl.server
package directives

import akka.annotation.DoNotInherit
import akka.http.impl.util._
import akka.http.scaladsl.common._
import akka.http.scaladsl.model.EntityStreamSizeException
Expand Down Expand Up @@ -126,8 +127,8 @@ object FormFieldDirectives extends FormFieldDirectives {
type Out
def apply(value: T): Out
}
object FieldDef {
def fieldDef[A, B](f: A B): FieldDefAux[A, B] =
object FieldDef extends LowLevelFieldDefImplicits {
protected def fieldDef[A, B](f: A B): FieldDefAux[A, B] =
new FieldDef[A] {
type Out = B
def apply(value: A) = f(value)
Expand All @@ -137,17 +138,101 @@ object FormFieldDirectives extends FormFieldDirectives {
import FutureDirectives._
import RouteDirectives._
import akka.http.scaladsl.unmarshalling.{ FromStrictFormFieldUnmarshaller FSFFU, _ }

type SFU = FromEntityUnmarshaller[StrictForm]
type FSFFOU[T] = Unmarshaller[Option[StrictForm.Field], T]

private def extractField[A, B](f: A Directive1[B]): FieldDefAux[A, Directive1[B]] = fieldDef(f)
private def handleFieldResult[T](fieldName: String, result: Future[T]): Directive1[T] = onComplete(result).flatMap {
protected def extractField[A, B](f: A Directive1[B]): FieldDefAux[A, Directive1[B]] = fieldDef(f)
protected def handleFieldResult[T](fieldName: String, result: Future[T]): Directive1[T] = onComplete(result).flatMap {
case Success(x) provide(x)
case Failure(Unmarshaller.NoContentException) reject(MissingFormFieldRejection(fieldName))
case Failure(x: UnsupportedContentTypeException) reject(UnsupportedRequestContentTypeRejection(x.supported))
case Failure(x) reject(MalformedFormFieldRejection(fieldName, x.getMessage.nullAsEmpty, Option(x.getCause)))
}

private def strictFormUnmarshaller(ctx: RequestContext): SFU =
StrictForm.unmarshaller(
Unmarshaller.defaultUrlEncodedFormDataUnmarshaller,
MultipartUnmarshallers.multipartFormDataUnmarshaller(ctx.log, ctx.parserSettings)
)
private val stringFromStrictForm: FSFFU[String] = StrictForm.Field.unmarshaller(StrictForm.Field.FieldUnmarshaller.stringFieldUnmarshaller)

private def fieldOfForm[T](fieldName: String, fu: Unmarshaller[Option[StrictForm.Field], T]): RequestContext Future[T] = { ctx
import ctx.{ executionContext, materializer }
strictFormUnmarshaller(ctx)(ctx.request.entity).fast.flatMap(form fu(form field fieldName))
}
private def filter[T](fieldName: String, fu: FSFFOU[T]): Directive1[T] =
extract(fieldOfForm(fieldName, fu)).flatMap(r handleFieldResult(fieldName, r))

implicit def forString: FieldDefAux[String, Directive1[String]] =
extractField[String, String] { fieldName filter(fieldName, stringFromStrictForm) }
implicit def forSymbol: FieldDefAux[Symbol, Directive1[String]] =
extractField[Symbol, String] { symbol filter(symbol.name, stringFromStrictForm) }
implicit def forNR[T](implicit fu: FSFFU[T]): FieldDefAux[NameReceptacle[T], Directive1[T]] =
extractField[NameReceptacle[T], T] { nr filter(nr.name, fu) }
implicit def forNUR[T]: FieldDefAux[NameUnmarshallerReceptacle[T], Directive1[T]] =
extractField[NameUnmarshallerReceptacle[T], T] { nr filter(nr.name, StrictForm.Field.unmarshallerFromFSU(nr.um)) }
implicit def forNOR[T](implicit fu: FSFFOU[T]): FieldDefAux[NameOptionReceptacle[T], Directive1[Option[T]]] =
extractField[NameOptionReceptacle[T], Option[T]] { nr filter[Option[T]](nr.name, fu) }
implicit def forNDR[T](implicit fu: FSFFOU[T]): FieldDefAux[NameDefaultReceptacle[T], Directive1[T]] =
extractField[NameDefaultReceptacle[T], T] { nr filter(nr.name, fu withDefaultValue nr.default) }
implicit def forNOUR[T]: FieldDefAux[NameOptionUnmarshallerReceptacle[T], Directive1[Option[T]]] =
extractField[NameOptionUnmarshallerReceptacle[T], Option[T]] { nr filter[Option[T]](nr.name, StrictForm.Field.unmarshallerFromFSU(nr.um): FSFFOU[T]) }
implicit def forNDUR[T]: FieldDefAux[NameDefaultUnmarshallerReceptacle[T], Directive1[T]] =
extractField[NameDefaultUnmarshallerReceptacle[T], T] { nr filter(nr.name, (StrictForm.Field.unmarshallerFromFSU(nr.um): FSFFOU[T]) withDefaultValue nr.default) }

//////////////////// required formField support ////////////////////

private def requiredFilter[T](fieldName: String, fu: Unmarshaller[Option[StrictForm.Field], T], requiredValue: Any): Directive0 =
extract(fieldOfForm(fieldName, fu)).flatMap {
onComplete(_).flatMap {
case Success(value) if value == requiredValue pass
case _ reject
}
}
implicit def forRVR[T](implicit fu: FSFFU[T]): FieldDefAux[RequiredValueReceptacle[T], Directive0] =
fieldDef[RequiredValueReceptacle[T], Directive0] { rvr requiredFilter(rvr.name, fu, rvr.requiredValue) }
implicit def forRVDR[T]: FieldDefAux[RequiredValueUnmarshallerReceptacle[T], Directive0] =
fieldDef[RequiredValueUnmarshallerReceptacle[T], Directive0] { rvr requiredFilter(rvr.name, StrictForm.Field.unmarshallerFromFSU(rvr.um), rvr.requiredValue) }

//////////////////// repeated formField support ////////////////////

private def repeatedFilter[T](fieldName: String, fu: FSFFU[T]): Directive1[Iterable[T]] =
extract { ctx
import ctx.{ executionContext, materializer }
strictFormUnmarshaller(ctx)(ctx.request.entity).fast.flatMap(form Future.sequence(form.fields.collect { case (`fieldName`, value) fu(value) }))
}.flatMap { result
handleFieldResult(fieldName, result)
}
implicit def forRepVR[T](implicit fu: FSFFU[T]): FieldDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] =
extractField[RepeatedValueReceptacle[T], Iterable[T]] { rvr repeatedFilter(rvr.name, fu) }
implicit def forRepVDR[T]: FieldDefAux[RepeatedValueUnmarshallerReceptacle[T], Directive1[Iterable[T]]] =
extractField[RepeatedValueUnmarshallerReceptacle[T], Iterable[T]] { rvr repeatedFilter(rvr.name, StrictForm.Field.unmarshallerFromFSU(rvr.um)) }

//////////////////// tuple support ////////////////////

import akka.http.scaladsl.server.util.BinaryPolyFunc
import akka.http.scaladsl.server.util.TupleOps._

implicit def forTuple[T](implicit fold: FoldLeft[Directive0, T, ConvertFieldDefAndConcatenate.type]): FieldDefAux[T, fold.Out] =
fieldDef[T, fold.Out](fold(pass, _))

object ConvertFieldDefAndConcatenate extends BinaryPolyFunc {
implicit def from[P, TA, TB](implicit fdef: FieldDefAux[P, Directive[TB]], ev: Join[TA, TB]): BinaryPolyFunc.Case[Directive[TA], P, ConvertFieldDefAndConcatenate.type] { type Out = Directive[ev.Out] } =
at[Directive[TA], P] { (a, t) a & fdef(t) }
}
}

@DoNotInherit
abstract class LowLevelFieldDefImplicits { self: FieldDef.type
import BasicDirectives._
import FutureDirectives._
import RouteDirectives._
import akka.http.scaladsl.unmarshalling.{ FromStrictFormFieldUnmarshaller FSFFU, _ }

// Old implicit definitions that should all have simpler alternatives above that don't require an implicit FSU, see #541
// Should be removed in next deprecation removal cycle.

//////////////////// "regular" formField extraction ////////////////////

private def fieldOfForm[T](fieldName: String, fu: Unmarshaller[Option[StrictForm.Field], T])(implicit sfu: SFU): RequestContext Future[T] = { ctx
Expand All @@ -156,20 +241,29 @@ object FormFieldDirectives extends FormFieldDirectives {
}
private def filter[T](fieldName: String, fu: FSFFOU[T])(implicit sfu: SFU): Directive1[T] =
extract(fieldOfForm(fieldName, fu)).flatMap(r handleFieldResult(fieldName, r))

@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forString(implicit sfu: SFU, fu: FSFFU[String]): FieldDefAux[String, Directive1[String]] =
extractField[String, String] { fieldName filter(fieldName, fu) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forSymbol(implicit sfu: SFU, fu: FSFFU[String]): FieldDefAux[Symbol, Directive1[String]] =
extractField[Symbol, String] { symbol filter(symbol.name, fu) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forNR[T](implicit sfu: SFU, fu: FSFFU[T]): FieldDefAux[NameReceptacle[T], Directive1[T]] =
extractField[NameReceptacle[T], T] { nr filter(nr.name, fu) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forNUR[T](implicit sfu: SFU): FieldDefAux[NameUnmarshallerReceptacle[T], Directive1[T]] =
extractField[NameUnmarshallerReceptacle[T], T] { nr filter(nr.name, StrictForm.Field.unmarshallerFromFSU(nr.um)) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forNOR[T](implicit sfu: SFU, fu: FSFFOU[T]): FieldDefAux[NameOptionReceptacle[T], Directive1[Option[T]]] =
extractField[NameOptionReceptacle[T], Option[T]] { nr filter[Option[T]](nr.name, fu) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forNDR[T](implicit sfu: SFU, fu: FSFFOU[T]): FieldDefAux[NameDefaultReceptacle[T], Directive1[T]] =
extractField[NameDefaultReceptacle[T], T] { nr filter(nr.name, fu withDefaultValue nr.default) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forNOUR[T](implicit sfu: SFU): FieldDefAux[NameOptionUnmarshallerReceptacle[T], Directive1[Option[T]]] =
extractField[NameOptionUnmarshallerReceptacle[T], Option[T]] { nr filter[Option[T]](nr.name, StrictForm.Field.unmarshallerFromFSU(nr.um): FSFFOU[T]) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forNDUR[T](implicit sfu: SFU): FieldDefAux[NameDefaultUnmarshallerReceptacle[T], Directive1[T]] =
extractField[NameDefaultUnmarshallerReceptacle[T], T] { nr filter(nr.name, (StrictForm.Field.unmarshallerFromFSU(nr.um): FSFFOU[T]) withDefaultValue nr.default) }

Expand All @@ -183,8 +277,10 @@ object FormFieldDirectives extends FormFieldDirectives {
case _ reject
}
}
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forRVR[T](implicit sfu: SFU, fu: FSFFU[T]): FieldDefAux[RequiredValueReceptacle[T], Directive0] =
fieldDef[RequiredValueReceptacle[T], Directive0] { rvr requiredFilter(rvr.name, fu, rvr.requiredValue) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forRVDR[T](implicit sfu: SFU): FieldDefAux[RequiredValueUnmarshallerReceptacle[T], Directive0] =
fieldDef[RequiredValueUnmarshallerReceptacle[T], Directive0] { rvr requiredFilter(rvr.name, StrictForm.Field.unmarshallerFromFSU(rvr.um), rvr.requiredValue) }

Expand All @@ -197,22 +293,11 @@ object FormFieldDirectives extends FormFieldDirectives {
}.flatMap { result
handleFieldResult(fieldName, result)
}
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forRepVR[T](implicit sfu: SFU, fu: FSFFU[T]): FieldDefAux[RepeatedValueReceptacle[T], Directive1[Iterable[T]]] =
extractField[RepeatedValueReceptacle[T], Iterable[T]] { rvr repeatedFilter(rvr.name, fu) }
@deprecated(message = "Customizing FromEntityUnmarshaller[StrictForm] is not supported any more. Use variant without the implicit SFU.", since = "10.0.11")
implicit def forRepVDR[T](implicit sfu: SFU): FieldDefAux[RepeatedValueUnmarshallerReceptacle[T], Directive1[Iterable[T]]] =
extractField[RepeatedValueUnmarshallerReceptacle[T], Iterable[T]] { rvr repeatedFilter(rvr.name, StrictForm.Field.unmarshallerFromFSU(rvr.um)) }

//////////////////// tuple support ////////////////////

import akka.http.scaladsl.server.util.BinaryPolyFunc
import akka.http.scaladsl.server.util.TupleOps._

implicit def forTuple[T](implicit fold: FoldLeft[Directive0, T, ConvertFieldDefAndConcatenate.type]): FieldDefAux[T, fold.Out] =
fieldDef[T, fold.Out](fold(pass, _))

object ConvertFieldDefAndConcatenate extends BinaryPolyFunc {
implicit def from[P, TA, TB](implicit fdef: FieldDefAux[P, Directive[TB]], ev: Join[TA, TB]): BinaryPolyFunc.Case[Directive[TA], P, ConvertFieldDefAndConcatenate.type] { type Out = Directive[ev.Out] } =
at[Directive[TA], P] { (a, t) a & fdef(t) }
}
}
}

0 comments on commit f5f49c4

Please sign in to comment.