Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow sending different auth headers to GitHub4s #875

Merged
merged 8 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import ProjectPlugin.on
import com.typesafe.tools.mima.core._

Global / onChangedBuildSource := ReloadOnSourceChanges

ThisBuild / organization := "com.47deg"

Expand All @@ -10,7 +13,12 @@ val allScalaVersions = scala2Versions :+ scala3Version
ThisBuild / scalaVersion := scala213
ThisBuild / crossScalaVersions := allScalaVersions

addCommandAlias("ci-test", "scalafmtCheckAll; scalafmtSbtCheck; mdoc; ++test")
disablePlugins(MimaPlugin)

addCommandAlias(
"ci-test",
"scalafmtCheckAll; scalafmtSbtCheck; mimaReportBinaryIssues; mdoc; ++test"
)
addCommandAlias("ci-docs", "github; mdoc; headerCreateAll; publishMicrosite")
addCommandAlias("ci-publish", "github; ci-release")

Expand All @@ -19,6 +27,7 @@ publish / skip := true
lazy val github4s = (crossProject(JSPlatform, JVMPlatform))
.crossType(CrossType.Full)
.withoutSuffixFor(JVMPlatform)
.enablePlugins(MimaPlugin)
.settings(coreDeps: _*)
.jsSettings(
// See the README for why this is necessary
Expand All @@ -30,7 +39,11 @@ lazy val github4s = (crossProject(JSPlatform, JVMPlatform))
// Increase number of inlines, needed for circe semiauto derivation
scalacOptions ++= on(3)(Seq("-Xmax-inlines", "48")).value.flatten,
// Disable nonunit warning on tests
Test / scalacOptions -= "-Wnonunit-statement"
Test / scalacOptions -= "-Wnonunit-statement",
mimaPreviousArtifacts := Set("com.47deg" %% "github4s" % "0.32.1"),
mimaBinaryIssueFilters ++= Seq(
ProblemFilters.exclude[IncompatibleMethTypeProblem]("github4s.http.HttpClient.this")
)
)

//////////
Expand All @@ -41,11 +54,13 @@ lazy val microsite: Project = project
.dependsOn(github4s.jvm)
.enablePlugins(MicrositesPlugin)
.enablePlugins(ScalaUnidocPlugin)
.disablePlugins(MimaPlugin)
.settings(micrositeSettings: _*)
.settings(publish / skip := true)
.settings(ScalaUnidoc / unidoc / unidocProjectFilter := inProjects(github4s.jvm, microsite))

lazy val documentation = project
.enablePlugins(MdocPlugin)
.disablePlugins(MimaPlugin)
.settings(mdocOut := file("."))
.settings(publish / skip := true)
23 changes: 4 additions & 19 deletions github4s/shared/src/main/scala/github4s/Github.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,21 @@

package github4s

import cats.effect.kernel.Concurrent
import cats.effect.Concurrent
import github4s.algebras._
import github4s.interpreters.StaticAccessToken
import github4s.modules._
import org.http4s.client.Client

@deprecated("Use github4s.GithubClient instead", "0.33.0")
class Github[F[_]: Concurrent](
client: Client[F],
accessToken: AccessToken[F]
)(implicit config: GithubConfig)
extends GithubAPIs[F] {

private lazy val module: GithubAPIs[F] = new GithubAPIv3[F](client, config, accessToken)

lazy val users: Users[F] = module.users
lazy val repos: Repositories[F] = module.repos
lazy val auth: Auth[F] = module.auth
lazy val gists: Gists[F] = module.gists
lazy val issues: Issues[F] = module.issues
lazy val activities: Activities[F] = module.activities
lazy val gitData: GitData[F] = module.gitData
lazy val pullRequests: PullRequests[F] = module.pullRequests
lazy val organizations: Organizations[F] = module.organizations
lazy val teams: Teams[F] = module.teams
lazy val projects: Projects[F] = module.projects
lazy val search: Search[F] = module.search
}
extends GithubClient[F](client, AccessHeader.from(accessToken))

object Github {

@deprecated("Use github4s.GithubClient instead", "0.33.0")
def apply[F[_]: Concurrent](
client: Client[F],
accessToken: Option[String] = None
Expand Down
60 changes: 60 additions & 0 deletions github4s/shared/src/main/scala/github4s/GithubClient.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2016-2023 47 Degrees Open Source <https://www.47deg.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package github4s

import cats.effect.Concurrent
import github4s.algebras._
import github4s.interpreters.StaticAccessToken
import github4s.modules._
import org.http4s.client.Client

private[github4s] class GithubClient[F[_]: Concurrent](
client: Client[F],
authHeader: AccessHeader[F]
)(implicit config: GithubConfig)
extends GithubAPIs[F] {

private lazy val module: GithubAPIs[F] = new GithubAPIsV3[F](client, config, authHeader)

lazy val users: Users[F] = module.users
lazy val repos: Repositories[F] = module.repos
lazy val auth: Auth[F] = module.auth
lazy val gists: Gists[F] = module.gists
lazy val issues: Issues[F] = module.issues
lazy val activities: Activities[F] = module.activities
lazy val gitData: GitData[F] = module.gitData
lazy val pullRequests: PullRequests[F] = module.pullRequests
lazy val organizations: Organizations[F] = module.organizations
lazy val teams: Teams[F] = module.teams
lazy val projects: Projects[F] = module.projects
lazy val search: Search[F] = module.search
}

object GithubClient {

def apply[F[_]: Concurrent](
client: Client[F],
accessToken: Option[String] = None
)(implicit config: GithubConfig): GithubAPIs[F] =
new GithubClient[F](client, AccessHeader.from(new StaticAccessToken(accessToken)))

def apply[F[_]: Concurrent](
client: Client[F],
authHeader: AccessHeader[F]
)(implicit config: GithubConfig): GithubAPIs[F] =
new GithubClient[F](client, authHeader)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package github4s.algebras

import github4s.GHResponse

trait AccessHeader[F[_]] {
def withAccessHeader[T](f: Map[String, String] => F[GHResponse[T]]): F[GHResponse[T]]
}

object AccessHeader {
def from[F[_]](accessToken: AccessToken[F]): AccessHeader[F] = new AccessHeader[F] {
override def withAccessHeader[T](f: Map[String, String] => F[GHResponse[T]]): F[GHResponse[T]] =
accessToken.withAccessToken { token =>
f(token.fold(Map.empty[String, String])(t => Map("Authorization" -> s"token $t")))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,5 @@ import github4s.GHResponse
* https://docs.github.com/en/free-pro-team@latest/developers/apps/authenticating-with-github-apps
*/
trait AccessToken[F[_]] {

def withAccessToken[T](f: Option[String] => F[GHResponse[T]]): F[GHResponse[T]]
}
54 changes: 34 additions & 20 deletions github4s/shared/src/main/scala/github4s/http/HttpClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import cats.effect.kernel.Concurrent
import cats.syntax.all._
import github4s.GHError._
import github4s._
import github4s.algebras.AccessToken
import github4s.algebras.{AccessHeader, AccessToken}
import github4s.domain.Pagination
import github4s.http.Http4sSyntax._
import io.circe.{Decoder, Encoder}
Expand All @@ -31,24 +31,24 @@ import org.http4s.circe.jsonOf
import org.http4s.client.Client
import org.http4s._

class HttpClient[F[_]: Concurrent](
class HttpClient[F[_]: Concurrent] private (
client: Client[F],
val config: GithubConfig,
accessTokens: AccessToken[F]
accessHeader: AccessHeader[F]
) {
import HttpClient._
import accessTokens._
import accessHeader._

def get[Res: Decoder](
method: String,
headers: Map[String, String] = Map.empty,
params: Map[String, String] = Map.empty,
pagination: Option[Pagination] = None
): F[GHResponse[Res]] =
withAccessToken { accessToken =>
withAccessHeader { authHeader =>
run[Unit, Res](
RequestBuilder(url = buildURL(method))
.withAuth(accessToken)
.withAuthHeader(authHeader)
.withHeaders(headers)
.withParams(
params ++ pagination.fold(Map.empty[String, String])(p =>
Expand All @@ -62,9 +62,9 @@ class HttpClient[F[_]: Concurrent](
url: String,
headers: Map[String, String] = Map.empty
): F[GHResponse[Unit]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
runWithoutResponse[Unit](
RequestBuilder(buildURL(url)).withHeaders(headers).withAuth(accessToken)
RequestBuilder(buildURL(url)).withHeaders(headers).withAuthHeader(authHeader)
)
)

Expand All @@ -73,10 +73,10 @@ class HttpClient[F[_]: Concurrent](
headers: Map[String, String] = Map.empty,
data: Req
): F[GHResponse[Res]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
run[Req, Res](
RequestBuilder(buildURL(method)).patchMethod
.withAuth(accessToken)
.withAuthHeader(authHeader)
.withHeaders(headers)
.withData(data)
)
Expand All @@ -87,10 +87,10 @@ class HttpClient[F[_]: Concurrent](
headers: Map[String, String] = Map(),
data: Req
): F[GHResponse[Res]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
run[Req, Res](
RequestBuilder(buildURL(url)).putMethod
.withAuth(accessToken)
.withAuthHeader(authHeader)
.withHeaders(headers)
.withData(data)
)
Expand All @@ -101,10 +101,10 @@ class HttpClient[F[_]: Concurrent](
headers: Map[String, String] = Map.empty,
data: Req
): F[GHResponse[Res]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
run[Req, Res](
RequestBuilder(buildURL(url)).postMethod
.withAuth(accessToken)
.withAuthHeader(authHeader)
.withHeaders(headers)
.withData(data)
)
Expand Down Expand Up @@ -132,20 +132,20 @@ class HttpClient[F[_]: Concurrent](
url: String,
headers: Map[String, String] = Map.empty
): F[GHResponse[Unit]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
run[Unit, Unit](
RequestBuilder(buildURL(url)).deleteMethod.withHeaders(headers).withAuth(accessToken)
RequestBuilder(buildURL(url)).deleteMethod.withHeaders(headers).withAuthHeader(authHeader)
)
)

def deleteWithResponse[Res: Decoder](
url: String,
headers: Map[String, String] = Map.empty
): F[GHResponse[Res]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
run[Unit, Res](
RequestBuilder(buildURL(url)).deleteMethod
.withAuth(accessToken)
.withAuthHeader(authHeader)
.withHeaders(headers)
)
)
Expand All @@ -155,10 +155,10 @@ class HttpClient[F[_]: Concurrent](
headers: Map[String, String] = Map.empty,
data: Req
): F[GHResponse[Res]] =
withAccessToken(accessToken =>
withAccessHeader(authHeader =>
run[Req, Res](
RequestBuilder(buildURL(url)).deleteMethod
.withAuth(accessToken)
.withAuthHeader(authHeader)
.withHeaders(headers)
.withData(data)
)
Expand Down Expand Up @@ -231,4 +231,18 @@ object HttpClient {

private def responseBody[F[_]: Concurrent](response: Response[F]): F[String] =
response.bodyText.compile.foldMonoid

def apply[F[_]: Concurrent](
client: Client[F],
config: GithubConfig,
accessToken: AccessToken[F]
): HttpClient[F] =
new HttpClient[F](client, config, AccessHeader.from(accessToken))

def apply[F[_]: Concurrent](
client: Client[F],
config: GithubConfig,
accessHeader: AccessHeader[F]
): HttpClient[F] =
new HttpClient[F](client, config, accessHeader)
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ case class RequestBuilder[Res](

def withData(data: Res): RequestBuilder[Res] = this.copy(data = Some(data))

def withAuthHeader(authHeader: Map[String, String]): RequestBuilder[Res] =
this.copy(authHeader = authHeader)

def withAuth(accessToken: Option[String] = None): RequestBuilder[Res] =
this.copy(authHeader = accessToken match {
case Some(token) => Map("Authorization" -> s"token $token")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package github4s.interpreters

import github4s.GHResponse
import github4s.algebras.AccessHeader

case class StaticAccessHeader[F[_]](accessHeader: Map[String, String]) extends AccessHeader[F] {
override def withAccessHeader[T](f: Map[String, String] => F[GHResponse[T]]): F[GHResponse[T]] =
f(accessHeader)
}

object StaticAccessHeader {
def noHeader[F[_]] = new StaticAccessHeader[F](Map.empty)
}
23 changes: 3 additions & 20 deletions github4s/shared/src/main/scala/github4s/modules/GithubAPIs.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,35 +19,18 @@ package github4s.modules
import cats.effect.kernel.Concurrent
import github4s.GithubConfig
import github4s.algebras._
import github4s.http.HttpClient
import github4s.interpreters._
import org.http4s.client.Client

@deprecated("Use github4s.modules.GithubAPIsV3 instead", "0.33.0")
class GithubAPIv3[F[_]: Concurrent](
client: Client[F],
config: GithubConfig,
accessToken: AccessToken[F]
) extends GithubAPIs[F] {

implicit val httpClient: HttpClient[F] = new HttpClient[F](client, config, accessToken)

override val users: Users[F] = new UsersInterpreter[F]
override val repos: Repositories[F] = new RepositoriesInterpreter[F]
override val auth: Auth[F] = new AuthInterpreter[F]
override val gists: Gists[F] = new GistsInterpreter[F]
override val issues: Issues[F] = new IssuesInterpreter[F]
override val activities: Activities[F] = new ActivitiesInterpreter[F]
override val gitData: GitData[F] = new GitDataInterpreter[F]
override val pullRequests: PullRequests[F] = new PullRequestsInterpreter[F]
override val organizations: Organizations[F] = new OrganizationsInterpreter[F]
override val teams: Teams[F] = new TeamsInterpreter[F]
override val projects: Projects[F] = new ProjectsInterpreter[F]
override val search: Search[F] = new SearchInterpreter[F]

}
) extends GithubAPIsV3[F](client, config, AccessHeader.from(accessToken))

object GithubAPIv3 {

@deprecated("Use github4s.modules.GithubAPIsV3.noAuth instead", "0.33.0")
def noAuth[F[_]: Concurrent](client: Client[F], config: GithubConfig): GithubAPIv3[F] =
new GithubAPIv3[F](client, config, StaticAccessToken.noToken)
}
Loading
Loading