如何在使用 Spray 反序列化具有类型参数的特征时对外部调用进行分页?
How to paginate external calls while deserialise a Trait with type parameters using Spray?
我有类似结构的对象
case class Countries(countries: Array[Country])
case class Users(users: Array[User])
case class States(states: Array[States])
上面提到的 类 用于填充来自外部 API 调用的数据:
class GenericHttpDataPuller[U:JsonReader, V:JsonReader](val endPoint:String) {
def getEntity[T:JsonReader](request: HttpRequest): Option[T] = {
Try(httpClient.singleRequest(httpRequest).entity.parseJson.convertTo[T]) match
{
case scala.util.Success(entityTry) => Some(entityTry)
case scala.util.Failure(exception) => None
}
}
def get(id:String, options: Map[String, String]): Option[U] = {
val uri = Uri(s"/$endPoint/$Id").withQuery(Uri.Query(queryMap.getOrElse(Map.empty[String,String])))
val httpRequest = HttpRequest(HttpMethods.GET,uri = uri,headers = additionalHeaders)
getEntity[U](httpRequest)
}
def getList(options: Map[String, String]): Option[V] = {
val uri = Uri(s"/$endPoint").withQuery(Uri.Query(queryMap.getOrElse(Map.empty[String,String])))
val httpRequest = HttpRequest(HttpMethods.GET,uri = uri,headers = additionalHeaders)
getEntity[V](httpRequest)
}
}
来电者
type HttpUserDataPuller = GenericHttpDataPuller[User, Users]
val users = new HttpUserDataPuller("users"). getList(None)
val user = new HttpUserDataPuller("users").get("someId", None)
以上代码工作正常。
我想根据 options
参数对 getList
方法进行分页调用。
我的更改与上述代码不兼容,
trait Pagination[V, U] {
def getSize(): Int
def getAppend(u: Option[U]): Option[U]
}
case class Users(users: Array[User]) extends Pagination[User, Users] {
override def getSize(): Int = users.size
override def getAppend(u: Option[Users]): Option[Users] = { // u is the return value from getEntity()
val users = u.map(that => that.users ++ this.users).getOrElse(Array.empty[User])
Some(Users(users)) }
}
getList
方法如下所示,使用 getSize
和 getAppend
方法我可以确定是否进行下一次调用(skip when getSize != current_page_size
),基于return 类型的 getEntity[PaginationAppender]
当我这样做时我的代码中断,因为我无法使用 Spary.
反序列化带有类型参数的 trait
def getList(options: Map[String, String]): Option[PaginationAppender[U, V]] = {
val limit = Try(queryMap.getOrElse(Map.empty[String, String]).getOrElse("limit", "-1").toInt).getOrElse(-1)
val uri = Uri(s"/$endPoint").withQuery(Uri.Query(queryMap.getOrElse(Map.empty[String,String])))
val httpRequest = HttpRequest(HttpMethods.GET,uri = uri,headers = additionalHeaders)
val returnValue = getEntity[PaginationAppender[U, V]]
def paginateData(currentPage: Option[PaginationAppender[U, V]]):Option[PaginationAppender[U, V]] = {
//page size is same as current request return value then go for next page
if(limit == currentPage.map(_.getSize()).getOrElse(0)) {
val nextPage = getEntity[PaginationAppender[U, V]](updatedRequsestForNextPage)
paginateData(currentPage.flatMap(now => now.append(nextPage)))
} else {
currentPage
}
paginateData(returnValue)
}
}
在不对现有结构做太多改变的情况下,有没有更好的解决这个问题的方法。
干杯!!!
使用了特征
trait PaginationAppender {
type T
def get(): Array[T]
}
由用户实现为
case class Users(users:Array[User]) extends PaginationAppender {
override type T = User
override def get(): Array[User] = this.users
}
分页方法
def paginateData[T:ClassTag, V <: PaginationAppender](httpUserDataPuller: GenericHttpDataPuller[T, V],
userId:String,
externalQueryMapOpt:Option[Map[String, String]] = None,
additionalHeaders:Option[Array[HttpHeader]] = None,
retry: Boolean = true,
limit: Int = 10,
offset: Int = 0
): ArraySeq[V#T] = {
val updatedExternalQueryMap = externalQueryMapOpt.getOrElse(Map.empty[String, String]) + ("limit" -> limit.toString) // add -OR- update limit to existing options
@tailrec
def getData(makeNextCall:Boolean, currentOffset: Int, existingData: ArraySeq[V#T] = ArraySeq.empty[V#T]): ArraySeq[V#T] = {
makeNextCall match {
case true =>
val currentPage = httpUserDataPuller.list(Some(updatedExternalQueryMap + ("offset" -> currentOffset.toString)), // add -OR- update offset to existing options
userId,
additionalHeaders,
retry).getOrElse(throw new IllegalArgumentException(s"Failed to list from generic pagination")).get()
getData(currentPage.size == limit, currentOffset + limit, existingData ++ currentPage)
case false => existingData
}
}
getData(true, offset) //first time call
}
来电者
paginateData[User, Users](new HttpUserDataPuller("users"), "user")
我有类似结构的对象
case class Countries(countries: Array[Country])
case class Users(users: Array[User])
case class States(states: Array[States])
上面提到的 类 用于填充来自外部 API 调用的数据:
class GenericHttpDataPuller[U:JsonReader, V:JsonReader](val endPoint:String) {
def getEntity[T:JsonReader](request: HttpRequest): Option[T] = {
Try(httpClient.singleRequest(httpRequest).entity.parseJson.convertTo[T]) match
{
case scala.util.Success(entityTry) => Some(entityTry)
case scala.util.Failure(exception) => None
}
}
def get(id:String, options: Map[String, String]): Option[U] = {
val uri = Uri(s"/$endPoint/$Id").withQuery(Uri.Query(queryMap.getOrElse(Map.empty[String,String])))
val httpRequest = HttpRequest(HttpMethods.GET,uri = uri,headers = additionalHeaders)
getEntity[U](httpRequest)
}
def getList(options: Map[String, String]): Option[V] = {
val uri = Uri(s"/$endPoint").withQuery(Uri.Query(queryMap.getOrElse(Map.empty[String,String])))
val httpRequest = HttpRequest(HttpMethods.GET,uri = uri,headers = additionalHeaders)
getEntity[V](httpRequest)
}
}
来电者
type HttpUserDataPuller = GenericHttpDataPuller[User, Users]
val users = new HttpUserDataPuller("users"). getList(None)
val user = new HttpUserDataPuller("users").get("someId", None)
以上代码工作正常。
我想根据 options
参数对 getList
方法进行分页调用。
我的更改与上述代码不兼容,
trait Pagination[V, U] {
def getSize(): Int
def getAppend(u: Option[U]): Option[U]
}
case class Users(users: Array[User]) extends Pagination[User, Users] {
override def getSize(): Int = users.size
override def getAppend(u: Option[Users]): Option[Users] = { // u is the return value from getEntity()
val users = u.map(that => that.users ++ this.users).getOrElse(Array.empty[User])
Some(Users(users)) }
}
getList
方法如下所示,使用 getSize
和 getAppend
方法我可以确定是否进行下一次调用(skip when getSize != current_page_size
),基于return 类型的 getEntity[PaginationAppender]
当我这样做时我的代码中断,因为我无法使用 Spary.
trait
def getList(options: Map[String, String]): Option[PaginationAppender[U, V]] = {
val limit = Try(queryMap.getOrElse(Map.empty[String, String]).getOrElse("limit", "-1").toInt).getOrElse(-1)
val uri = Uri(s"/$endPoint").withQuery(Uri.Query(queryMap.getOrElse(Map.empty[String,String])))
val httpRequest = HttpRequest(HttpMethods.GET,uri = uri,headers = additionalHeaders)
val returnValue = getEntity[PaginationAppender[U, V]]
def paginateData(currentPage: Option[PaginationAppender[U, V]]):Option[PaginationAppender[U, V]] = {
//page size is same as current request return value then go for next page
if(limit == currentPage.map(_.getSize()).getOrElse(0)) {
val nextPage = getEntity[PaginationAppender[U, V]](updatedRequsestForNextPage)
paginateData(currentPage.flatMap(now => now.append(nextPage)))
} else {
currentPage
}
paginateData(returnValue)
}
}
在不对现有结构做太多改变的情况下,有没有更好的解决这个问题的方法。
干杯!!!
使用了特征
trait PaginationAppender {
type T
def get(): Array[T]
}
由用户实现为
case class Users(users:Array[User]) extends PaginationAppender {
override type T = User
override def get(): Array[User] = this.users
}
分页方法
def paginateData[T:ClassTag, V <: PaginationAppender](httpUserDataPuller: GenericHttpDataPuller[T, V],
userId:String,
externalQueryMapOpt:Option[Map[String, String]] = None,
additionalHeaders:Option[Array[HttpHeader]] = None,
retry: Boolean = true,
limit: Int = 10,
offset: Int = 0
): ArraySeq[V#T] = {
val updatedExternalQueryMap = externalQueryMapOpt.getOrElse(Map.empty[String, String]) + ("limit" -> limit.toString) // add -OR- update limit to existing options
@tailrec
def getData(makeNextCall:Boolean, currentOffset: Int, existingData: ArraySeq[V#T] = ArraySeq.empty[V#T]): ArraySeq[V#T] = {
makeNextCall match {
case true =>
val currentPage = httpUserDataPuller.list(Some(updatedExternalQueryMap + ("offset" -> currentOffset.toString)), // add -OR- update offset to existing options
userId,
additionalHeaders,
retry).getOrElse(throw new IllegalArgumentException(s"Failed to list from generic pagination")).get()
getData(currentPage.size == limit, currentOffset + limit, existingData ++ currentPage)
case false => existingData
}
}
getData(true, offset) //first time call
}
来电者
paginateData[User, Users](new HttpUserDataPuller("users"), "user")