Skip to content

Commit c6562af

Browse files
committed
Add Query from Franklin
1 parent 7d1c22b commit c6562af

File tree

3 files changed

+126
-13
lines changed

3 files changed

+126
-13
lines changed

build.sbt

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -206,17 +206,23 @@ lazy val client = (project in file("modules/client"))
206206
.dependsOn(core)
207207
.settings(commonSettings)
208208
.settings(publishSettings)
209-
.settings(libraryDependencies ++= coreDependencies)
210209
.settings(
211210
libraryDependencies ++= Seq(
212-
"co.fs2" %% "fs2-core" % "2.4.2",
213-
"org.http4s" %% "http4s-blaze-client" % "0.21.7",
214-
"org.http4s" %% "http4s-circe" % "0.21.7",
215-
"org.http4s" %% "http4s-client" % "0.21.7",
216-
"org.http4s" %% "http4s-core" % "0.21.7",
217-
"org.typelevel" %% "cats-effect" % "2.1.4",
218-
"org.typelevel" %% "cats-effect" % "2.1.4",
219-
"io.chrisdavenport" %% "vault" % "2.0.0",
220-
"io.chrisdavenport" %% "log4cats-core" % "1.1.1"
211+
"io.circe" %% "circe-core" % Versions.CirceVersion,
212+
"io.circe" %% "circe-generic" % Versions.CirceVersion,
213+
"io.circe" %% "circe-refined" % Versions.CirceVersion,
214+
"com.chuusai" %% "shapeless" % Versions.ShapelessVersion,
215+
"eu.timepit" %% "refined" % Versions.RefinedVersion,
216+
"org.locationtech.geotrellis" %% "geotrellis-vector" % Versions.GeoTrellisVersion,
217+
"org.locationtech.jts" % "jts-core" % Versions.jts,
218+
"org.typelevel" %% "cats-core" % Versions.CatsVersion,
219+
"co.fs2" %% "fs2-core" % "2.4.2",
220+
"org.http4s" %% "http4s-blaze-client" % "0.21.7",
221+
"org.http4s" %% "http4s-circe" % "0.21.7",
222+
"org.http4s" %% "http4s-client" % "0.21.7",
223+
"org.http4s" %% "http4s-core" % "0.21.7",
224+
"org.typelevel" %% "cats-effect" % "2.1.4",
225+
"io.chrisdavenport" %% "vault" % "2.0.0",
226+
"io.chrisdavenport" %% "log4cats-core" % "1.1.1"
221227
)
222228
)
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.azavea.stac4s.api.client
2+
3+
import cats.data.NonEmptyVector
4+
import cats.implicits._
5+
import eu.timepit.refined.types.string.NonEmptyString
6+
import io.circe._
7+
import io.circe.syntax._
8+
import io.circe.refined._
9+
10+
sealed abstract class Query
11+
12+
case class Equals(value: Json) extends Query
13+
case class NotEqualTo(value: Json) extends Query
14+
case class GreaterThan(floor: Json) extends Query
15+
case class GreaterThanEqual(floor: Json) extends Query
16+
case class LessThan(ceiling: Json) extends Query
17+
case class LessThanEqual(ceiling: Json) extends Query
18+
case class StartsWith(prefix: NonEmptyString) extends Query
19+
case class EndsWith(postfix: NonEmptyString) extends Query
20+
case class Contains(substring: NonEmptyString) extends Query
21+
case class In(values: NonEmptyVector[Json]) extends Query
22+
case class Superset(values: NonEmptyVector[Json]) extends Query
23+
24+
object Query {
25+
26+
private def fromString(s: String, f: NonEmptyString => Query): Option[Query] =
27+
NonEmptyString.from(s).toOption.map(f)
28+
29+
private def fromStringOrNum(js: Json, f: Json => Query): Option[Query] =
30+
js.asNumber map { _ =>
31+
f(js)
32+
} orElse { js.asString map { _ => f(js) } }
33+
34+
private def errMessage(operator: String, json: Json): String =
35+
s"Cannot construct `$operator` query with $json"
36+
37+
def queriesFromMap(unparsed: Map[String, Json]): Either[String, List[Query]] =
38+
(unparsed.toList traverse {
39+
case (op @ "eq", json) => Right(Equals(json))
40+
case (op @ "neq", json) => Right(NotEqualTo(json))
41+
case (op @ "lt", json) =>
42+
Either.fromOption(fromStringOrNum(json, LessThan.apply), errMessage(op, json))
43+
case (op @ "lte", json) =>
44+
Either.fromOption(
45+
fromStringOrNum(json, LessThanEqual.apply),
46+
errMessage(op, json)
47+
)
48+
case (op @ "gt", json) =>
49+
Either.fromOption(
50+
fromStringOrNum(json, GreaterThan.apply),
51+
errMessage(op, json)
52+
)
53+
case (op @ "gte", json) =>
54+
Either.fromOption(
55+
fromStringOrNum(json, GreaterThanEqual.apply),
56+
errMessage(op, json)
57+
)
58+
case (op @ "startsWith", json) =>
59+
Either.fromOption(
60+
json.asString flatMap { fromString(_, StartsWith.apply) },
61+
errMessage(op, json)
62+
)
63+
case (op @ "endsWith", json) =>
64+
Either.fromOption(
65+
json.asString flatMap { fromString(_, EndsWith.apply) },
66+
errMessage(op, json)
67+
)
68+
case (op @ "contains", json) =>
69+
Either.fromOption(
70+
json.asString flatMap { fromString(_, Contains.apply) },
71+
errMessage(op, json)
72+
)
73+
case (op @ "in", json) =>
74+
Either.fromOption(
75+
json.asArray flatMap { _.toNev } map { vec => In(vec) },
76+
errMessage(op, json)
77+
)
78+
case (op @ "superset", json) =>
79+
Either.fromOption(
80+
json.asArray flatMap { _.toNev } map { vec => Superset(vec) },
81+
errMessage(op, json)
82+
)
83+
case (k, _) => Left(s"$k is not a valid operator")
84+
})
85+
86+
implicit val encQuery: Encoder[List[Query]] = new Encoder[List[Query]] {
87+
88+
def apply(queries: List[Query]): Json =
89+
Map(
90+
(queries map {
91+
case Equals(value) => "eq" -> value.asJson
92+
case NotEqualTo(value) => "neq" -> value.asJson
93+
case GreaterThan(floor) => "gt" -> floor.asJson
94+
case GreaterThanEqual(floor) => "gte" -> floor.asJson
95+
case LessThan(ceiling) => "lt" -> ceiling.asJson
96+
case LessThanEqual(ceiling) => "lte" -> ceiling.asJson
97+
case StartsWith(prefix) => "startsWith" -> prefix.asJson
98+
case EndsWith(postfix) => "endsWith" -> postfix.asJson
99+
case Contains(substring) => "contains" -> substring.asJson
100+
case In(values) => "in" -> values.asJson
101+
case Superset(values) => "superset" -> values.asJson
102+
}): _*
103+
).asJson
104+
}
105+
106+
implicit val decQueries: Decoder[List[Query]] = Decoder[JsonObject].emap { jsonObj => queriesFromMap(jsonObj.toMap) }
107+
}

modules/client/src/main/scala/com/azavea/stac4s/api/client/SearchFilters.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ case class SearchFilters(
1919
collections: List[String] = Nil,
2020
items: List[String] = Nil,
2121
limit: Option[NonNegInt] = None,
22-
query: JsonObject = JsonObject.empty,
22+
query: Map[String, List[Query]] = Map.empty,
2323
next: Option[PaginationToken] = None
2424
)
2525

@@ -80,7 +80,7 @@ object SearchFilters {
8080
collectionsOption <- c.downField("collections").as[Option[List[String]]]
8181
itemsOption <- c.downField("items").as[Option[List[String]]]
8282
limit <- c.downField("limit").as[Option[NonNegInt]]
83-
query <- c.get[Option[JsonObject]]("query")
83+
query <- c.get[Option[Map[String, List[Query]]]]("query")
8484
paginationToken <- c.get[Option[PaginationToken]]("next")
8585
} yield {
8686
SearchFilters(
@@ -90,7 +90,7 @@ object SearchFilters {
9090
collectionsOption.getOrElse(Nil),
9191
itemsOption.getOrElse(Nil),
9292
limit,
93-
query.getOrElse(JsonObject.empty),
93+
query getOrElse Map.empty,
9494
paginationToken
9595
)
9696
}

0 commit comments

Comments
 (0)