Skip to content

Commit

Permalink
Use sttp monad error (#336)
Browse files Browse the repository at this point in the history
* remove redundant toTry conversion

* use sttp MonadError

* add migration guide

* silent mima
  • Loading branch information
bwiercinski committed Jan 9, 2023
1 parent 509a4af commit 12ba5b2
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 49 deletions.
27 changes: 15 additions & 12 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -73,18 +73,21 @@ def compilerPlugins =
libraryDependencies ++= (if (scalaVersion.value.startsWith("3")) Seq()
else Seq(compilerPlugin("com.olegpy" %% "better-monadic-for" % "0.3.1")))

val mimaSettings =
mimaPreviousArtifacts := {
val currentVersion = version.value
lazy val onlyPatchChanged =
previousStableVersion.value.flatMap(CrossVersion.partialVersion) == CrossVersion.partialVersion(currentVersion)
lazy val isRcOrMilestone = currentVersion.contains("-M") || currentVersion.contains("-RC")
if (onlyPatchChanged && !isRcOrMilestone) {
previousStableVersion.value.map(organization.value %% moduleName.value % _).toSet
} else {
Set.empty
}
}
val mimaSettings = {
// revert the commit that made this change after releasing a new version
//mimaPreviousArtifacts := {
// val currentVersion = version.value
// lazy val onlyPatchChanged =
// previousStableVersion.value.flatMap(CrossVersion.partialVersion) == CrossVersion.partialVersion(currentVersion)
// lazy val isRcOrMilestone = currentVersion.contains("-M") || currentVersion.contains("-RC")
// if (onlyPatchChanged && !isRcOrMilestone) {
// previousStableVersion.value.map(organization.value %% moduleName.value % _).toSet
// } else {
// Set.empty
// }
//}
mimaPreviousArtifacts := Set.empty
}

// Workaround for https://github.com/typelevel/sbt-tpolecat/issues/102
val jsSettings = scalacOptions ++= (if (scalaVersion.value.startsWith("3")) Seq("-scalajs") else Seq())
Expand Down
9 changes: 9 additions & 0 deletions docs/migrating.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ description: Migrations

Some releases introduce breaking changes. This page aims to list those and provide migration guide.

## [v0.16.0](https://github.com/ocadotechnology/sttp-oauth2/releases/tag/v0.16.0)

Minor change [#336](https://github.com/ocadotechnology/sttp-oauth2/pull/336) removed implicit parameter
of `cats.MonadThrow` in some methods. As long as your code just uses these methods (doesn't override or mock
interfaces), you have to only solve warnings suggesting that there are unused parameters. Otherwise,
remove `: MonadError` from inherited implementations.

Affected classes: `PasswordGrant`, `PasswordGrantProvider`, `SttpOauth2ClientCredentialsBackend`, `UserInfoProvider`

## [v0.15.0](https://github.com/ocadotechnology/sttp-oauth2/releases/tag/v0.15.0)

### Breaking change in authorization code grant
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ object AccessTokenProvider {
override def requestToken(scope: Option[Scope]): F[ClientCredentialsToken.AccessTokenResponse] =
ClientCredentials
.requestToken(tokenUrl, clientId, clientSecret, scope)(backend)
.map(_.leftMap(OAuth2Exception.apply).toTry)
.flatMap(backend.responseMonad.fromTry)
.map(_.leftMap(OAuth2Exception.apply))
.flatMap(_.fold(F.error, F.unit))

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ object AuthorizationCode {
)(
backend: SttpBackend[F, Any]
): F[RT] = {
implicit val ME: MonadError[F] = backend.responseMonad
implicit val F: MonadError[F] = backend.responseMonad
backend
.send {
basicRequest
Expand All @@ -54,15 +54,8 @@ object AuthorizationCode {
.response(asString)
.header(HeaderNames.Accept, "application/json")
}
.flatMap { response =>
ME.fromTry(
response
.body
.leftMap(new RuntimeException(_))
.flatMap(decode[RT])
.toTry
)
}
.map(_.body.leftMap(new RuntimeException(_)).flatMap(decode[RT]))
.flatMap(_.fold(F.error, F.unit))
}

private def tokenRequestParams(authCode: String, redirectUri: String, clientId: String, clientSecret: String) =
Expand Down Expand Up @@ -91,8 +84,8 @@ object AuthorizationCode {
.body(refreshTokenRequestParams(refreshToken, clientId, clientSecret.value, scopeOverride.toRequestMap))
.response(asString)
}
.map(_.body.leftMap(new RuntimeException(_)).flatMap(decode[RT]).toTry)
.flatMap(backend.responseMonad.fromTry)
.map(_.body.leftMap(new RuntimeException(_)).flatMap(decode[RT]))
.flatMap(_.fold(F.error, F.unit))
}

private def refreshTokenRequestParams(refreshToken: String, clientId: String, clientSecret: String, scopeOverride: Map[String, String]) =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.ocadotechnology.sttp.oauth2

import cats.Functor
import common._
import com.ocadotechnology.sttp.oauth2.common._
import eu.timepit.refined.types.string.NonEmptyString
import sttp.client3._
import sttp.model.Uri
import cats.syntax.all._
import sttp.monad.MonadError
import sttp.monad.syntax._

object PasswordGrant {

Expand All @@ -22,15 +22,16 @@ object PasswordGrant {

}

def requestToken[F[_]: Functor](
def requestToken[F[_]](
tokenUri: Uri,
user: User,
clientId: NonEmptyString,
clientSecret: Secret[String],
scope: Scope
)(
backend: SttpBackend[F, Any]
): F[OAuth2Token.Response] =
): F[OAuth2Token.Response] = {
implicit val F: MonadError[F] = backend.responseMonad
backend
.send {
basicRequest
Expand All @@ -39,6 +40,7 @@ object PasswordGrant {
.response(OAuth2Token.response)
}
.map(_.body)
}

private def requestTokenParams(clientId: NonEmptyString, user: User, clientSecret: Secret[String], scope: Scope) =
Map(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.ocadotechnology.sttp.oauth2

import cats.MonadThrow
import common._
import cats.syntax.all._
import com.ocadotechnology.sttp.oauth2.PasswordGrant.User
import com.ocadotechnology.sttp.oauth2.common._
import eu.timepit.refined.types.string.NonEmptyString
import sttp.client3.SttpBackend
import sttp.model.Uri
import cats.syntax.all._
import sttp.monad.MonadError
import sttp.monad.syntax._

trait PasswordGrantProvider[F[_]] {
def requestToken(user: User, scope: Scope): F[ExtendedOAuth2TokenResponse]
Expand All @@ -16,14 +17,18 @@ object PasswordGrantProvider {

def apply[F[_]](implicit ev: PasswordGrantProvider[F]): PasswordGrantProvider[F] = ev

def apply[F[_]: MonadThrow](
def apply[F[_]](
tokenUrl: Uri,
clientId: NonEmptyString,
clientSecret: Secret[String]
)(
backend: SttpBackend[F, Any]
): PasswordGrantProvider[F] = { (user: User, scope: Scope) =>
PasswordGrant.requestToken(tokenUrl, user, clientId, clientSecret, scope)(backend).map(_.leftMap(OAuth2Exception.apply)).rethrow
implicit val F: MonadError[F] = backend.responseMonad
PasswordGrant
.requestToken(tokenUrl, user, clientId, clientSecret, scope)(backend)
.map(_.leftMap(OAuth2Exception.apply))
.flatMap(_.fold(F.error, F.unit))
}

}
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package com.ocadotechnology.sttp.oauth2

import cats.Monad
import cats.implicits._
import com.ocadotechnology.sttp.oauth2.common.Scope
import eu.timepit.refined.types.string.NonEmptyString
import sttp.capabilities.Effect
import sttp.client3._
import sttp.model.Uri
import sttp.monad.MonadError
import sttp.monad.syntax._

/** SttpBackend, that adds auth bearer headers to every request.
*/
final class SttpOauth2ClientCredentialsBackend[F[_]: Monad, P] private (
final class SttpOauth2ClientCredentialsBackend[F[_], P] private (
delegate: SttpBackend[F, P],
accessTokenProvider: AccessTokenProvider[F],
scope: Option[common.Scope]
) extends DelegateSttpBackend(delegate) {
implicit val F: MonadError[F] = delegate.responseMonad

override def send[T, R >: P with Effect[F]](request: Request[T, R]): F[Response[T]] = for {
token <- accessTokenProvider.requestToken(scope)
Expand All @@ -25,7 +26,7 @@ final class SttpOauth2ClientCredentialsBackend[F[_]: Monad, P] private (

object SttpOauth2ClientCredentialsBackend {

def apply[F[_]: Monad, P](
def apply[F[_], P](
tokenUrl: Uri,
clientId: NonEmptyString,
clientSecret: Secret[String]
Expand All @@ -38,7 +39,7 @@ object SttpOauth2ClientCredentialsBackend {
SttpOauth2ClientCredentialsBackend(accessTokenProvider)(scope)(backend)
}

def apply[F[_]: Monad, P](
def apply[F[_], P](
accessTokenProvider: AccessTokenProvider[F]
)(
scope: Option[Scope]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ object TokenIntrospection {
override def introspect(token: Secret[String]): F[Introspection.TokenIntrospectionResponse] =
ClientCredentials
.introspectToken(tokenIntrospectionUrl, clientId, clientSecret, token)(backend)
.map(_.leftMap(OAuth2Exception.apply).toTry)
.flatMap(backend.responseMonad.fromTry)
.map(_.leftMap(OAuth2Exception.apply))
.flatMap(_.fold(F.error, F.unit))

}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.ocadotechnology.sttp.oauth2

import cats.MonadThrow
import cats.syntax.all._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.string.Url
import io.circe.parser.decode
import sttp.client3._
import sttp.model.Uri
import sttp.monad.MonadError
import sttp.monad.syntax._

trait UserInfoProvider[F[_]] {
def userInfo(accessToken: String): F[UserInfo]
Expand All @@ -15,12 +16,13 @@ trait UserInfoProvider[F[_]] {
object UserInfoProvider {
def apply[F[_]](implicit ev: UserInfoProvider[F]): UserInfoProvider[F] = ev

private def requestUserInfo[F[_]: MonadThrow](
private def requestUserInfo[F[_]](
baseUrl: Uri,
accessToken: String
)(
backend: SttpBackend[F, Any]
): F[UserInfo] =
): F[UserInfo] = {
implicit val F: MonadError[F] = backend.responseMonad
backend
.send {
basicRequest
Expand All @@ -29,18 +31,19 @@ object UserInfoProvider {
.response(asString)
}
.map(_.body.leftMap(new RuntimeException(_)).flatMap(decode[UserInfo]))
.rethrow
.flatMap(_.fold(F.error, F.unit))
}

// TODO - add some description on what is expected of baseUrl
def apply[F[_]: MonadThrow](
def apply[F[_]](
baseUrl: Uri
)(
backend: SttpBackend[F, Any]
): UserInfoProvider[F] =
(accessToken: String) => requestUserInfo(baseUrl, accessToken)(backend)

// TODO - add some description on what is expected of baseUrl
def apply[F[_]: MonadThrow](
def apply[F[_]](
baseUrl: String Refined Url
)(
backend: SttpBackend[F, Any]
Expand Down

0 comments on commit 12ba5b2

Please sign in to comment.