Scala wrapper around the Google Sheets API
gsheets4s is available for Scala 2.12 on Maven central at the following coordinates:
"com.github.benfradet" %% "gsheets4s" % "0.2.0"
gsheets4s uses OAuth 2.0 to authenticate requests made to Google servers, the process to get your OAuth 2.0 credentials is detailed at: https://developers.google.com/identity/protocols/OAuth2ForDevices.
Out of this process, gsheets4s needs:
- an initial access token (it will take care of refreshing it when needed)
- a refresh token
- a client ID
- a client secret
The last three are needed in order to refresh the access token as it is valid for only one hour.
Here's a program you could write that updates and consecutively gets contents from a spreadhseet:
import cats.Monad
import cats.data.EitherT
import gsheets4s.algebras.SpreadsheetsValues
import gsheets4s.model._
class Program[F[_]: Monad](alg: SpreadsheetsValues[F]) {
import alg._
def updateAndGet(
spreadsheetId: String,
vr: ValueRange,
vio: ValueInputOption
): F[Either[GsheetsError, (UpdateValuesResponse, ValueRange)]] =
(for {
updateValuesResponse <- EitherT(update(spreadsheetId, vr.range, vr, vio))
valueRange <- EitherT(get(spreadsheetId, vr.range))
} yield (updateValuesResponse, valueRange)).value
}
And here's how you could use it leveraging cats-effect's Sync
:
import cats.effect.Sync
import cats.effect.concurrent.Ref
import cats.syntax.flatMap._
import cats.syntax.functor._
import eu.timepit.refined.auto._
import gsheets4s.GSheets4s
import gsheets4s.model._
class GSheetsService[F[_]: Sync](credentials: Ref[F, Credentials]) {
val spreadsheetID = "1tk2S_A4LZfeZjoMskbfFXO42_b75A7UkSdhKaQZlDmA"
val valueRange = {
val notation = RangeNotation(Range(ColRowPosition("A", 1), ColRowPosition("B", 2)))
ValueRange(notation, Rows, List(List("1", "2"), List("3", "4")))
}
def updateAndGet: F[Either[GsheetsError, List[String]]] = for {
spreadsheetsValues <- Sync[F].pure(GSheets4s[F](credentials).spreadsheetsValues)
prog <- new Program[F](spreadsheetsValues)
.updateAndGet(spreadsheetID, valueRange, UserEntered)
res = prog.map(_._2.values.flatten)
} yield res
}
object GSheetsService {
def apply[F[_]: Sync](credentials: Credentials): F[GSheetsService[F]] =
Ref.of[F, Credentials](credentials)
.map(ref => new GSheetsService[F](ref))
}
Finally, here's how you could run it using cats-effect's IO
:
import cats.effect.IO
object Main {
def main(args: Array[String]): Unit = {
val creds = Credentials(accessToken, refreshToken, clientId, clientSecret)
val io = for {
service <- GSheetsService[IO](creds)
res <- service.updateAndGet
} yield res
io.unsafeRunSync()
}
}
Here's the list of currently supported endpoints:
-
-
batchUpdate
-
create
-
get
-
getByDataFilter
-
-
spreadsheets.developerMetadata
-
get
-
search
-
-
-
copyTo
-
-
-
append
-
batchClear
-
batchClearByDataFilter
-
batchGet
-
batchGetByDataFilter
-
batchUpdate
-
batchUpdateByDataFilter
-
clear
-
get
-
update
-
gsheets4s abstracts over the effect type F
by using tagless final encoding for easy composition
between the different APIs and to leave the choice of effect type to the user as long as you provide
an instance of cats.effect.Sync
for your effect type F
.
gsheets4s uses refined and atto to make sure A1 notation is respected at compile-time when you refer to a group of cells in a spreadsheet.
By providing a refresh token, we make sure that your access token is always valid, refreshing it if needed.
Here are the projects I got inspiration from when building gsheets4s:
Here are the projects used in the library: