Skip to content

Commit

Permalink
Pagination Improvements (#496)
Browse files Browse the repository at this point in the history
* Improve Pagination

* Bump deps versions
  • Loading branch information
pomadchin authored Mar 29, 2022
1 parent 84d7c90 commit ddaf02c
Show file tree
Hide file tree
Showing 9 changed files with 99 additions and 55 deletions.
2 changes: 1 addition & 1 deletion .scalafmt.conf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
runner.dialect = scala212
version = 3.3.1
version = 3.3.3
align.preset = more // For pretty alignment.
maxColumn = 120
newlines.topLevelStatementBlankLines = [
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## [Unreleased]
### Changed
- Pagination Improvements [#496](https://github.com/azavea/stac4s/pull/496)

## [0.7.2] - 2021-10-12
### Changed
Expand Down
6 changes: 3 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ lazy val commonSettings = Seq(
git.gitDescribedVersion.value.get
},
scalaVersion := "2.12.15",
crossScalaVersions := List("2.12.15", "2.13.7"),
crossScalaVersions := List("2.12.15", "2.13.8"),
Global / cancelable := true,
scalafmtOnCompile := true,
ThisBuild / scapegoatVersion := Versions.Scapegoat,
Expand Down Expand Up @@ -109,7 +109,7 @@ lazy val credentialSettings = Seq(

val jvmGeometryDependencies = Def.setting {
Seq(
"org.locationtech.jts" % "jts-core" % Versions.Jts.value,
"org.locationtech.jts" % "jts-core" % Versions.Jts,
"org.locationtech.geotrellis" %% "geotrellis-vector" % Versions.GeoTrellis.value
)
}
Expand All @@ -124,7 +124,7 @@ val coreDependenciesJVM = Def.setting {
val testingDependenciesJVM = Def.setting {
Seq(
"org.locationtech.geotrellis" %% "geotrellis-vector" % Versions.GeoTrellis.value,
"org.locationtech.jts" % "jts-core" % Versions.Jts.value,
"org.locationtech.jts" % "jts-core" % Versions.Jts,
"org.threeten" % "threeten-extra" % Versions.ThreeTenExtra
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.azavea.stac4s.api.client

import com.azavea.stac4s.api.client.SttpStacClientF.PaginationToken
import com.azavea.stac4s.api.client.util.syntax._
import com.azavea.stac4s.{StacCollection, StacItem, StacLink, StacLinkType}

Expand All @@ -13,15 +12,14 @@ import cats.syntax.nested._
import cats.syntax.option._
import eu.timepit.refined.types.string.NonEmptyString
import fs2.Stream
import io.circe.refined._
import io.circe.syntax._
import io.circe.{Encoder, Json, JsonObject}
import monocle.Lens
import sttp.client3.circe.asJson
import sttp.client3.{Response, SttpBackend, UriContext, basicRequest}
import sttp.model.{MediaType, Uri}

case class SttpStacClientF[F[_]: MonadThrow, S: Lens[*, Option[PaginationToken]]: Encoder](
case class SttpStacClientF[F[_]: MonadThrow, S: Encoder](
client: SttpBackend[F, Any],
baseUri: Uri
) extends StreamingStacClientF[F, Stream[F, *], S] {
Expand All @@ -47,7 +45,7 @@ case class SttpStacClientF[F[_]: MonadThrow, S: Lens[*, Option[PaginationToken]]
)
.flatMap { response =>
val items = response.stacItems
val next = response.nextPage(filter)
val next = response.nextPage(request)
(items, next).tupled
}
}
Expand Down Expand Up @@ -154,8 +152,8 @@ object SttpStacClientF {

implicit class ResponseEitherJsonOps[E <: Exception](val self: Response[Either[E, Json]]) extends AnyVal {

/** Get the next page Uri from the retrieved Json body and the next PaginationToken. */
def nextPage[F[_]: MonadThrow]: F[Option[(Uri, Option[PaginationToken])]] =
/** Get the next page Uri from the retrieved Json body and the next pagination body. */
def nextPage[F[_]: MonadThrow]: F[Option[(Uri, Option[Json])]] =
self.body
.flatMap {
_.hcursor
Expand All @@ -168,23 +166,21 @@ object SttpStacClientF {
// Some STAC API implementations (i.e. Franklin)
// encode pagination into the next page Uri (put it into the l.href):
// in this case, the pagination token is always set to None and only Uri is used for the pagination purposes.
val paginationToken: Option[PaginationToken] =
l
.extensionFields("body")
.flatMap(_.asObject)
.flatMap(_("next"))
.flatMap(_.as[PaginationToken].toOption)

uri"${l.href}" -> paginationToken

// to make the case described above more generic, we can take the entire body
// and pass it forward by merging with the body (SearchFilters in a form of Json)
// with the paginationBody
// see https://github.com/azavea/stac4s/pull/496 for details
val paginationBody: Option[Json] = l.extensionFields("body").map(_.deepDropNullValues)

uri"${l.href}" -> paginationBody
}))
}
.liftTo[F]

/** Get the next page Uri and the next page Json request body (that has a correctly set next page token). */
def nextPage[F[_]: MonadThrow, S](
filter: Option[S]
)(implicit l: Lens[S, Option[PaginationToken]], enc: Encoder[S]): F[Option[(Uri, Json)]] =
nextPage.nested.map { case (uri, token) => (uri, filter.setPaginationToken(token)) }.value
def nextPage[F[_]: MonadThrow](filter: Json): F[Option[(Uri, Json)]] =
nextPage.nested.map { case (uri, body) => (uri, filter.setPaginationBody(body)) }.value

/** Get the next page Uri and drop the next page token / body. Useful for get requests with no POST pagination
* support.
Expand All @@ -207,4 +203,26 @@ object SttpStacClientF {
)(implicit l: Lens[S, Option[PaginationToken]], enc: Encoder[S]): Json =
self.map(l.set(token)(_).asJson).getOrElse(JsonObject.empty.asJson)
}

implicit class JsonOps[S](val self: Json) extends AnyVal {

def setPaginationBody(body: Option[Json]): Json = {
val selfNotNull = self.deepDropNullValues
val bodyNotNull = body.map(_.deepDropNullValues).getOrElse(JsonObject.empty.asJson)

val filter = selfNotNull.deepMerge(bodyNotNull)

// bbox and intersection can't be present at the same time
if (filter.hcursor.downField("bbox").succeeded && filter.hcursor.downField("intersects").succeeded) {
// let's see which field is present in the nextPageBody
val field =
if (bodyNotNull.hcursor.downField("bbox").succeeded && bodyNotNull.hcursor.downField("intersects").failed)
filter.hcursor.downField("intersects")
else
filter.hcursor.downField("bbox")

field.delete.top.getOrElse(filter)
} else filter
}
}
}
8 changes: 4 additions & 4 deletions project/Versions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ object Versions {
val CirceJsonSchema = "0.2.0"
val DisciplineScalatest = "2.1.5"
val Enumeratum = "1.7.0"
val GeoTrellis = Def.setting(ver("3.6.0", "3.6.1-SNAPSHOT").value)
val Jts = Def.setting(ver("1.16.1", "1.17.0").value)
val GeoTrellis = Def.setting(ver("3.6.1", "3.6.1+2-9cc96096-SNAPSHOT").value)
val Jts = "1.18.1"
val Monocle = "2.1.0"
val Refined = "0.9.28"
val ScalacheckCats = "0.3.1"
val Scalacheck = "1.15.4"
val ScalatestPlusScalacheck = "3.2.2.0"
val Scalatest = "3.2.10"
val Scapegoat = "1.4.11"
val Scalatest = "3.2.11"
val Scapegoat = "1.4.12"
val Shapeless = "2.3.7"
val Sttp = "3.3.18"
val SttpModel = "1.4.18"
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.6.1
sbt.version=1.6.2
14 changes: 7 additions & 7 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.10")
addSbtPlugin("org.xerial.sbt" % "sbt-sonatype" % "3.9.12")
addSbtPlugin("com.github.cb372" % "sbt-explicit-dependencies" % "0.2.16")
addSbtPlugin("io.crashbox" % "sbt-gpg" % "0.2.1")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "1.0.2")
addSbtPlugin("com.github.sbt" % "sbt-git" % "2.0.0")
addSbtPlugin("io.spray" % "sbt-revolver" % "0.9.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6")
addSbtPlugin("com.sksamuel.scapegoat" %% "sbt-scapegoat" % "1.1.1")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.33")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.1.0")
addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.20")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.9.34")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.2.0")
addSbtPlugin("io.github.davidgregory084" % "sbt-tpolecat" % "0.1.22")
addSbtPlugin("org.jmotor.sbt" % "sbt-dependency-updates" % "1.2.2")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.8.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.1.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.9.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0")
24 changes: 18 additions & 6 deletions sbt
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@

set -o pipefail

declare -r sbt_release_version="1.5.2"
declare -r sbt_unreleased_version="1.5.2"
declare -r sbt_release_version="1.6.2"
declare -r sbt_unreleased_version="1.6.2"

declare -r latest_213="2.13.5"
declare -r latest_212="2.12.13"
declare -r latest_213="2.13.8"
declare -r latest_212="2.12.15"
declare -r latest_211="2.11.12"
declare -r latest_210="2.10.7"
declare -r latest_29="2.9.3"
Expand Down Expand Up @@ -216,7 +216,8 @@ getJavaVersion() {
# but on 9 and 10 it's 9.x.y and 10.x.y.
if [[ "$str" =~ ^1\.([0-9]+)(\..*)?$ ]]; then
echo "${BASH_REMATCH[1]}"
elif [[ "$str" =~ ^([0-9]+)(\..*)?$ ]]; then
# Fixes https://github.com/dwijnand/sbt-extras/issues/326
elif [[ "$str" =~ ^([0-9]+)(\..*)?(-ea)?$ ]]; then
echo "${BASH_REMATCH[1]}"
elif [[ -n "$str" ]]; then
echoerr "Can't parse java version from: $str"
Expand Down Expand Up @@ -252,7 +253,9 @@ is_apple_silicon() { [[ "$(uname -s)" == "Darwin" && "$(uname -m)" == "arm64" ]]
# MaxPermSize critical on pre-8 JVMs but incurs noisy warning on 8+
default_jvm_opts() {
local -r v="$(java_version)"
if [[ $v -ge 10 ]]; then
if [[ $v -ge 17 ]]; then
echo "$default_jvm_opts_common"
elif [[ $v -ge 10 ]]; then
if is_apple_silicon; then
# As of Dec 2020, JVM for Apple Silicon (M1) doesn't support JVMCI
echo "$default_jvm_opts_common"
Expand Down Expand Up @@ -385,10 +388,12 @@ usage() {
set_sbt_version
cat <<EOM
Usage: $script_name [options]
Note that options which are passed along to sbt begin with -- whereas
options to this runner use a single dash. Any sbt command can be scheduled
to run first by prefixing the command with --, so --warn, --error and so on
are not special.
-h | -help print this message
-v verbose operation (this runner is chattier)
-d, -w, -q aliases for --debug, --warn, --error (q means quiet)
Expand All @@ -406,13 +411,15 @@ are not special.
-batch Disable interactive mode
-prompt <expr> Set the sbt prompt; in expr, 's' is the State and 'e' is Extracted
-script <file> Run the specified file as a scala script
# sbt version (default: sbt.version from $buildProps if present, otherwise $sbt_release_version)
-sbt-version <version> use the specified version of sbt (default: $sbt_release_version)
-sbt-force-latest force the use of the latest release of sbt: $sbt_release_version
-sbt-dev use the latest pre-release version of sbt: $sbt_unreleased_version
-sbt-jar <path> use the specified jar as the sbt launcher
-sbt-launch-dir <path> directory to hold sbt launchers (default: $sbt_launch_dir)
-sbt-launch-repo <url> repo url for downloading sbt launcher jar (default: $(url_base "$sbt_version"))
# scala version (default: as chosen by sbt)
-28 use $latest_28
-29 use $latest_29
Expand All @@ -423,8 +430,10 @@ are not special.
-scala-home <path> use the scala build at the specified directory
-scala-version <version> use the specified version of scala
-binary-version <version> use the specified scala version when searching for dependencies
# java version (default: java from PATH, currently $(java -version 2>&1 | grep version))
-java-home <path> alternate JAVA_HOME
# passing options to the jvm - note it does NOT use JAVA_OPTS due to pollution
# The default set is used if JVM_OPTS is unset and no -jvm-opts file is found
<default> $(default_jvm_opts)
Expand All @@ -434,12 +443,14 @@ are not special.
-jvm-opts <path> file containing jvm args (if not given, .jvmopts in project root is used if present)
-Dkey=val pass -Dkey=val directly to the jvm
-J-X pass option -X directly to the jvm (-J is stripped)
# passing options to sbt, OR to this runner
SBT_OPTS environment variable holding either the sbt args directly, or
the reference to a file containing sbt args if given path is prepended by '@' (e.g. '@/etc/sbtopts')
Note: "@"-file is overridden by local '.sbtopts' or '-sbt-opts' argument.
-sbt-opts <path> file containing sbt args (if not given, .sbtopts in project root is used if present)
-S-X add -X to sbt's scalacOptions (-S is stripped)
# passing options exclusively to this runner
SBTX_OPTS environment variable holding either the sbt-extras args directly, or
the reference to a file containing sbt-extras args if given path is prepended by '@' (e.g. '@/etc/sbtxopts')
Expand Down Expand Up @@ -592,6 +603,7 @@ fi
$(pwd) doesn't appear to be an sbt project.
If you want to start sbt anyway, run:
$0 -sbt-create
EOM
exit 1
}
Expand Down
42 changes: 27 additions & 15 deletions scripts/test
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,32 @@ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
usage
else
echo "Linting Scala source code and executing tests"
./sbt "++$SCALA_VERSION" ";\
scalafix --check; \
scalafmtCheck; \
scalafmtSbtCheck; \
scapegoat; \
coreJVM/undeclaredCompileDependenciesTest; \
testingJVM/undeclaredCompileDependenciesTest; \
coreTestJVM/undeclaredCompileDependenciesTest; \
clientJVM/undeclaredCompileDependenciesTest; \
coreJVM/unusedCompileDependenciesTest; \
testingJVM/unusedCompileDependenciesTest; \
coreTestJVM/unusedCompileDependenciesTest; \
clientJVM/unusedCompileDependenciesTest; \
test
"

# due to a specific deps in the class path, we verify declared deps only for the 2.12 builds
if [[ "$SCALA_VERSION" =~ ^2.12.* ]]; then
./sbt "++$SCALA_VERSION" ";\
scalafix --check; \
scalafmtCheck; \
scalafmtSbtCheck; \
scapegoat; \
coreJVM/undeclaredCompileDependenciesTest; \
testingJVM/undeclaredCompileDependenciesTest; \
coreTestJVM/undeclaredCompileDependenciesTest; \
clientJVM/undeclaredCompileDependenciesTest; \
coreJVM/unusedCompileDependenciesTest; \
testingJVM/unusedCompileDependenciesTest; \
coreTestJVM/unusedCompileDependenciesTest; \
clientJVM/unusedCompileDependenciesTest; \
test
"
else
./sbt "++$SCALA_VERSION" ";\
scalafix --check; \
scalafmtCheck; \
scalafmtSbtCheck; \
scapegoat; \
test
"
fi
fi
fi

0 comments on commit ddaf02c

Please sign in to comment.