-
Notifications
You must be signed in to change notification settings - Fork 11
Сlient module #140
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
Сlient module #140
Changes from all commits
7d1c22b
c6562af
7630412
31a0bf0
c732bae
be07703
739912d
7a275a2
ed8fda8
46d9c57
0469396
8821a2f
f060644
c44cffc
1d8a5f8
0647db7
2733d69
7377a3f
990ad20
d587f46
fdee543
7f6abce
1d83880
baa5c6c
30123e3
6d2ab01
c708009
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.azavea.stac4s.api.client | ||
jisantuc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
import com.azavea.stac4s.Bbox | ||
import com.azavea.stac4s.api.client.utils.ClientCodecs | ||
import com.azavea.stac4s.geometry.Geometry | ||
import com.azavea.stac4s.types.TemporalExtent | ||
|
||
import eu.timepit.refined.types.numeric.NonNegInt | ||
import io.circe._ | ||
import io.circe.generic.semiauto._ | ||
import io.circe.refined._ | ||
|
||
case class SearchFilters( | ||
bbox: Option[Bbox] = None, | ||
datetime: Option[TemporalExtent] = None, | ||
intersects: Option[Geometry] = None, | ||
collections: List[String] = Nil, | ||
items: List[String] = Nil, | ||
limit: Option[NonNegInt] = None, | ||
query: Map[String, List[Query]] = Map.empty, | ||
next: Option[PaginationToken] = None | ||
) | ||
|
||
object SearchFilters extends ClientCodecs { | ||
|
||
implicit val searchFilterDecoder: Decoder[SearchFilters] = { c => | ||
for { | ||
bbox <- c.downField("bbox").as[Option[Bbox]] | ||
datetime <- c.downField("datetime").as[Option[TemporalExtent]] | ||
intersects <- c.downField("intersects").as[Option[Geometry]] | ||
collectionsOption <- c.downField("collections").as[Option[List[String]]] | ||
itemsOption <- c.downField("items").as[Option[List[String]]] | ||
limit <- c.downField("limit").as[Option[NonNegInt]] | ||
query <- c.get[Option[Map[String, List[Query]]]]("query") | ||
paginationToken <- c.get[Option[PaginationToken]]("next") | ||
} yield { | ||
SearchFilters( | ||
bbox, | ||
datetime, | ||
intersects, | ||
collectionsOption.getOrElse(Nil), | ||
itemsOption.getOrElse(Nil), | ||
limit, | ||
query getOrElse Map.empty, | ||
paginationToken | ||
) | ||
} | ||
} | ||
|
||
implicit val searchFilterEncoder: Encoder[SearchFilters] = deriveEncoder | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.azavea.stac4s.api.client | ||
|
||
import cats.MonadError | ||
import sttp.client3.SttpBackend | ||
import sttp.model.Uri | ||
|
||
object SttpStacClient { | ||
|
||
def apply[F[_]: MonadError[*[_], Throwable]]( | ||
client: SttpBackend[F, Any], | ||
baseUri: Uri | ||
): SttpStacClient[F] = | ||
SttpStacClientF.instance[F, SearchFilters](client, baseUri) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.azavea.stac4s.api | ||
|
||
package object client { | ||
type SttpStacClient[F[_]] = SttpStacClientF.Aux[F, SearchFilters] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.azavea.stac4s.api.client | ||
|
||
import com.azavea.stac4s.testing.JsInstances | ||
|
||
import sttp.client3.UriContext | ||
|
||
class SttpStacClientSpec extends SttpStacClientFSpec with JsInstances { | ||
lazy val client = SttpStacClient(backend, uri"http://localhost:9090") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.azavea.stac4s.api.client | ||
|
||
import com.azavea.stac4s.Bbox | ||
import com.azavea.stac4s.api.client.utils.ClientCodecs | ||
import com.azavea.stac4s.types.TemporalExtent | ||
|
||
import eu.timepit.refined.types.numeric.NonNegInt | ||
import geotrellis.vector.{io => _, _} | ||
import io.circe._ | ||
import io.circe.generic.semiauto._ | ||
import io.circe.refined._ | ||
|
||
case class SearchFilters( | ||
bbox: Option[Bbox] = None, | ||
datetime: Option[TemporalExtent] = None, | ||
intersects: Option[Geometry] = None, | ||
collections: List[String] = Nil, | ||
items: List[String] = Nil, | ||
limit: Option[NonNegInt] = None, | ||
query: Map[String, List[Query]] = Map.empty, | ||
next: Option[PaginationToken] = None | ||
) | ||
|
||
object SearchFilters extends ClientCodecs { | ||
|
||
implicit val searchFilterDecoder: Decoder[SearchFilters] = { c => | ||
for { | ||
bbox <- c.downField("bbox").as[Option[Bbox]] | ||
datetime <- c.downField("datetime").as[Option[TemporalExtent]] | ||
intersects <- c.downField("intersects").as[Option[Geometry]] | ||
collectionsOption <- c.downField("collections").as[Option[List[String]]] | ||
itemsOption <- c.downField("items").as[Option[List[String]]] | ||
limit <- c.downField("limit").as[Option[NonNegInt]] | ||
query <- c.get[Option[Map[String, List[Query]]]]("query") | ||
paginationToken <- c.get[Option[PaginationToken]]("next") | ||
} yield { | ||
SearchFilters( | ||
bbox, | ||
datetime, | ||
intersects, | ||
collectionsOption.getOrElse(Nil), | ||
itemsOption.getOrElse(Nil), | ||
limit, | ||
query getOrElse Map.empty, | ||
paginationToken | ||
) | ||
} | ||
} | ||
|
||
implicit val searchFilterEncoder: Encoder[SearchFilters] = deriveEncoder | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package com.azavea.stac4s.api.client | ||
|
||
import cats.MonadError | ||
import sttp.client3.SttpBackend | ||
import sttp.model.Uri | ||
|
||
object SttpStacClient { | ||
|
||
def apply[F[_]: MonadError[*[_], Throwable]]( | ||
client: SttpBackend[F, Any], | ||
baseUri: Uri | ||
): SttpStacClient[F] = | ||
SttpStacClientF.instance[F, SearchFilters](client, baseUri) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.azavea.stac4s.api | ||
|
||
package object client { | ||
type SttpStacClient[F[_]] = SttpStacClientF.Aux[F, SearchFilters] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.azavea.stac4s.api.client | ||
|
||
import com.azavea.stac4s.testing.JvmInstances | ||
|
||
import sttp.client3.UriContext | ||
|
||
class SttpStacClientSpec extends SttpStacClientFSpec with JvmInstances { | ||
lazy val client = SttpStacClient(backend, uri"http://localhost:9090") | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.azavea.stac4s.api.client | ||
|
||
import cats.syntax.either._ | ||
import eu.timepit.refined.types.numeric.PosInt | ||
import io.circe | ||
import io.circe.generic.semiauto._ | ||
import io.circe.parser.parse | ||
import io.circe.refined._ | ||
import io.circe.syntax._ | ||
import io.circe.{Decoder, Encoder} | ||
|
||
import java.time.Instant | ||
import java.util.Base64 | ||
|
||
final case class PaginationToken(timestampAtLeast: Instant, serialIdGreaterThan: PosInt) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is correct for Franklin's implementation of pagination, but only for Franklin's -- the particular pagination strategy is left open to implementers. So there are a few things that are a bit weird as a result --
So I think this shouldn't work There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say this is fine; since it is a default implementation (i.e. if smth doesn't work user can always define theirs own STACClient with a redefined search filters: see https://github.com/azavea/stac4s/blob/7f6abceeebea59dfc9a605891c73fc121116610b/modules/client/jvm/src/main/scala/com/azavea/stac4s/api/client/SttpStacClient.scala#L9-L13 - they only need to define their own version of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opened an issue for that #198 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not really talking about streaming here though -- I don't think this pagination encoding will work, and the tests included here don't exercise pagination. Since the pagination implementation here isn't exercised and I think won't work, I think it should just be omitted until someone is working on #198 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops forgot to push not tested codecs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
/** Circe codecs should encode token into a base64 string | ||
* https://github.com/azavea/franklin/blob/f5be8ddf48661c5bc43cbd22cb7277e961641803/application/src/main/scala/com/azavea/franklin/api/schemas/package.scala#L84-L85 | ||
*/ | ||
object PaginationToken { | ||
val b64Encoder = Base64.getEncoder | ||
val b64Decoder = Base64.getDecoder | ||
|
||
val defaultDecoder: Decoder[PaginationToken] = deriveDecoder | ||
val defaultEncoder: Encoder[PaginationToken] = deriveEncoder | ||
|
||
def encPaginationToken(token: PaginationToken): String = b64Encoder.encodeToString( | ||
token.asJson(defaultEncoder).noSpaces.getBytes | ||
) | ||
|
||
def decPaginationToken(encoded: String): Either[circe.Error, PaginationToken] = { | ||
val jsonString = new String(b64Decoder.decode(encoded)) | ||
for { | ||
js <- parse(jsonString) | ||
decoded <- js.as[PaginationToken](defaultDecoder) | ||
} yield decoded | ||
} | ||
|
||
implicit val dec: Decoder[PaginationToken] = | ||
Decoder.decodeString.emap(str => decPaginationToken(str).leftMap(_.getMessage)) | ||
|
||
implicit val enc: Encoder[PaginationToken] = { encPaginationToken(_).asJson } | ||
} |
Uh oh!
There was an error while loading. Please reload this page.