Skip to content

Commit

Permalink
Merge pull request #1089 from softwaremill/interceptors
Browse files Browse the repository at this point in the history
Implements #1065: Interceptors
  • Loading branch information
adamw authored Mar 22, 2021
2 parents 609c3c7 + 9ce3cea commit c152a8f
Show file tree
Hide file tree
Showing 125 changed files with 2,823 additions and 3,397 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ val booksListingRoute: Route = AkkaHttpServerInterpreter
import sttp.tapir.client.sttp.SttpClientInterpreter
import sttp.client3._

val booksListingRequest: Request[DecodeResult[Either[String, List[Book]]], Any] = SttpClientInterpreter
.toRequest(booksListing, Some(uri"http://localhost:8080"))
.apply((BooksFromYear("SF", 2016), 20, "xyz-abc-123"))
val booksListingRequest: Request[DecodeResult[Either[String, List[Book]]], Any] =
SttpClientInterpreter
.toRequest(booksListing, Some(uri"http://localhost:8080"))
.apply((BooksFromYear("SF", 2016), 20, "xyz-abc-123"))
```

## Documentation
Expand Down
15 changes: 7 additions & 8 deletions core/src/main/scala/sttp/tapir/model/ServerRequest.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
package sttp.tapir.model

import java.net.{InetSocketAddress, URI}
import sttp.model.{QueryParams, RequestMetadata}
import java.net.InetSocketAddress

import sttp.model.Method

trait ServerRequest {
def method: Method
trait ServerRequest extends RequestMetadata {
def protocol: String
def uri: URI
def connectionInfo: ConnectionInfo
def headers: Seq[(String, String)]
def header(name: String): Option[String]
def underlying: Any

/** Can differ from `uri.path`, if the endpoint is deployed in a context */
def pathSegments: List[String]
def queryParameters: QueryParams
}

case class ConnectionInfo(local: Option[InetSocketAddress], remote: Option[InetSocketAddress], secure: Option[Boolean])
10 changes: 10 additions & 0 deletions core/src/main/scala/sttp/tapir/model/ServerResponse.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package sttp.tapir.model

import sttp.model.{Header, Headers, ResponseMetadata, StatusCode}

import scala.collection.immutable.Seq

case class ServerResponse[B](code: StatusCode, headers: Seq[Header], body: Option[B]) extends ResponseMetadata {
override def statusText: String = ""
override def toString: String = s"ServerResponse($code,${Headers.toStringSafe(headers)})"
}

This file was deleted.

16 changes: 0 additions & 16 deletions core/src/main/scala/sttp/tapir/server/DecodeFailureHandling.scala

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package sttp.tapir.server.interceptor

import sttp.tapir.model.ServerRequest
import sttp.tapir.{DecodeResult, Endpoint, EndpointInput}

case class DecodeFailureContext(
failingInput: EndpointInput[_],
failure: DecodeResult.Failure,
endpoint: Endpoint[_, _, _, _],
request: ServerRequest
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package sttp.tapir.server.interceptor

import sttp.monad.MonadError
import sttp.tapir.model.{ServerRequest, ServerResponse}
import sttp.tapir.{DecodeResult, Endpoint, EndpointInput}

/** Allows intercepting the handling of a request by an endpoint, when either the endpoint's inputs have been
* decoded successfully, or when decoding has failed.
* @tparam B The interpreter-specific, low-level type of body.
*/
trait EndpointInterceptor[F[_], B] {

/** Called when the the given `request` has been successfully decoded into inputs `i`, as described by
* `endpoint.input`.
*
* Use `next(None)` to continue processing, ultimately (after the last interceptor) calling the endpoint's server
* logic, and obtaining a response. Or, provide an alternative value+output pair, which will be used as the response.
*
* @tparam I The type of the endpoint's inputs.
* @return An effect, describing the server's response.
*/
def onDecodeSuccess[I](
request: ServerRequest,
endpoint: Endpoint[I, _, _, _],
i: I,
next: Option[ValuedEndpointOutput[_]] => F[ServerResponse[B]]
)(implicit monad: MonadError[F]): F[ServerResponse[B]] = next(None)

/** Called when the the given `request` hasn't been successfully decoded into inputs `i`, as described by `endpoint`,
* with `failure` occurring when decoding `failingInput`.
*
* Use `next(None)` to continue processing, ultimately (after the last interceptor) returning `None`, and attempting
* to decode the next endpoint. Or, provide an alternative value+output pair, which will be used as the response.
*
* @return An effect, describing the optional server response. If `None`, the next endpoint will be tried (if any).
*/
def onDecodeFailure(
request: ServerRequest,
endpoint: Endpoint[_, _, _, _],
failure: DecodeResult.Failure,
failingInput: EndpointInput[_],
next: Option[ValuedEndpointOutput[_]] => F[Option[ServerResponse[B]]]
)(implicit monad: MonadError[F]): F[Option[ServerResponse[B]]] = next(None)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package sttp.tapir.server.interceptor

import sttp.tapir.EndpointOutput

case class ValuedEndpointOutput[T](output: EndpointOutput[T], value: T)
Loading

0 comments on commit c152a8f

Please sign in to comment.