Skip to content

Commit b252d4e

Browse files
author
Hariharan Ramanathan
committed
Refactoring search command to sttp
1 parent 1de2c18 commit b252d4e

File tree

4 files changed

+95
-90
lines changed

4 files changed

+95
-90
lines changed

src/main/scala/de/upb/cs/swt/delphi/cli/DelphiCLI.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ object DelphiCLI {
4949
config.mode match {
5050
case "test" => TestCommand.execute
5151
case "retrieve" => RetrieveCommand.execute
52-
// case "search" => SearchCommand.execute(config)
52+
case "search" => SearchCommand.execute
5353
case x => config.consoleOutput.outputError(s"Unknown command: $x")
5454
}
5555

src/main/scala/de/upb/cs/swt/delphi/cli/commands/RetrieveCommand.scala

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import scala.io.Source
2727
* The implementation of the retrieve command.
2828
* Retrieves the contents of the file at the endpoint specified by the config file, and prints them to stdout
2929
*/
30-
object RetrieveCommand extends Command with DefaultJsonProtocol {
30+
object RetrieveCommand extends Command {
3131

3232

3333
override def execute(implicit config: Config, backend: SttpBackend[Id, Nothing]): Unit = {
@@ -45,7 +45,6 @@ object RetrieveCommand extends Command with DefaultJsonProtocol {
4545
}
4646
}
4747

48-
println(config.id)
4948
val result = executeGet(
5049
Seq("retrieve", checkTarget),
5150
Map("pretty" -> "")

src/main/scala/de/upb/cs/swt/delphi/cli/commands/SearchCommand.scala

Lines changed: 65 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -16,124 +16,102 @@
1616

1717
package de.upb.cs.swt.delphi.cli.commands
1818

19-
import java.util.concurrent.{TimeUnit, TimeoutException}
20-
21-
import akka.actor.ActorSystem
22-
import akka.http.scaladsl.Http
23-
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
24-
import akka.http.scaladsl.marshalling.Marshal
25-
import akka.http.scaladsl.model._
26-
import akka.http.scaladsl.unmarshalling.Unmarshal
27-
import akka.stream.ActorMaterializer
28-
import akka.util.ByteString
29-
import de.upb.cs.swt.delphi.cli.Config
19+
import java.util.concurrent.TimeUnit
20+
21+
import com.softwaremill.sttp._
22+
import com.softwaremill.sttp.sprayJson._
3023
import de.upb.cs.swt.delphi.cli.artifacts.SearchResult
31-
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultJson._
32-
import spray.json.DefaultJsonProtocol
24+
import de.upb.cs.swt.delphi.cli.{Config, artifacts}
25+
import spray.json._
3326

3427
import scala.concurrent.duration._
35-
import scala.concurrent.{Await, ExecutionContextExecutor, Future}
36-
import scala.util.{Failure, Success, Try}
3728

38-
object SearchCommand extends Command with SprayJsonSupport with DefaultJsonProtocol {
29+
object SearchCommand extends Command with DefaultJsonProtocol{
3930

40-
val searchTimeout: Int = 10
31+
val searchTimeout = 10.seconds
32+
val TIMEOUT_CODE = 408
4133

4234
/**
4335
* Executes the command implementation
4436
*
4537
* @param config The current configuration for the command
4638
*/
47-
def execute(config: Config)(implicit system: ActorSystem): Unit = {
48-
implicit val ec = system.dispatcher
49-
implicit val materializer = ActorMaterializer()
39+
override def execute(implicit config: Config, backend: SttpBackend[Id, Nothing]): Unit = {
5040

5141
def query = config.query
5242

53-
information(config)(s"Searching for artifacts matching ${'"'}$query${'"'}.")
54-
val start = System.nanoTime()
43+
information.apply(s"Searching for artifacts matching ${'"'}$query${'"'}.")
5544

56-
implicit val queryFormat = jsonFormat2(Query)
57-
val baseUri = Uri(config.server)
58-
val prettyParam = Map("pretty" -> "")
59-
val searchUri = baseUri.withPath(baseUri.path + "/search").withQuery(akka.http.scaladsl.model.Uri.Query(prettyParam))
60-
val responseFuture = Marshal(Query(query, config.limit)).to[RequestEntity] flatMap { entity =>
61-
Http().singleRequest(HttpRequest(uri = searchUri, method = HttpMethods.POST, entity = entity))
62-
}
6345

64-
Try(Await.result(responseFuture, Duration(config.timeout.getOrElse(searchTimeout) + " seconds"))).
65-
map(response => parseResponse(response, config, start)(ec, materializer)).
66-
recover {
67-
case e : TimeoutException => {
68-
error(config)("The query timed out after " + (System.nanoTime() - start).nanos.toUnit(TimeUnit.SECONDS) +
69-
" seconds. To set a longer timeout, use the --timeout option.")
70-
Failure(e)
71-
}
72-
}
46+
val queryParams = Map("pretty" -> "")
47+
val queryPayload: Query = Query(query,config.limit)
48+
val searchUri = uri"${config.server}/search?$queryParams"
49+
50+
val request = sttp.body(queryPayload.toJson).post(searchUri)
51+
52+
val (res, time) = processRequest(request)
53+
res.foreach(processResults(_, time))
7354
}
7455

75-
private def parseResponse(response: HttpResponse, config: Config, start: Long)
76-
(implicit ec: ExecutionContextExecutor, materializer: ActorMaterializer): Unit = {
56+
private def processRequest(req: Request[String, Nothing])
57+
(implicit config: Config,
58+
backend: SttpBackend[Id, Nothing]): (Option[String], FiniteDuration) = {
59+
val start = System.nanoTime()
60+
val res: Id[Response[String]] = req.readTimeout(searchTimeout).send()
61+
val end = System.nanoTime()
62+
val took = (end - start).nanos
7763

78-
val resultFuture: Future[String] = response match {
79-
case HttpResponse(StatusCodes.OK, headers, entity, _) =>
80-
entity.dataBytes.runFold(ByteString(""))(_ ++ _).map { body =>
81-
body.utf8String
82-
}
83-
case resp@HttpResponse(code, _, _, _) => {
84-
error(config)("Request failed, response code: " + code)
85-
resp.discardEntityBytes()
86-
Future("")
87-
}
88-
}
64+
if (res.code == TIMEOUT_CODE) {
8965

90-
val result = Await.result(resultFuture, Duration.Inf)
66+
error.apply(s"The query timed out after ${took.toSeconds} seconds. " +
67+
"To set a longer timeout, use the --timeout option.")
68+
}
69+
val resStr = res.body match {
70+
case Left(v) =>
71+
error.apply(s"Search request failed \n $v")
72+
println(v)
73+
None
74+
case Right(v) =>
75+
Some(v)
76+
}
77+
(resStr, took)
78+
}
9179

92-
val took = (System.nanoTime() - start).nanos.toUnit(TimeUnit.SECONDS)
80+
private def processResults(res: String, queryRuntime: FiniteDuration)(implicit config: Config) = {
9381

94-
if (config.raw || result.equals("")) {
95-
reportResult(config)(result)
82+
if (config.raw || res.equals("")) {
83+
reportResult.apply(res)
84+
}
85+
if (!(config.raw || res.equals("")) || !config.csv.equals("")) {
86+
import artifacts.SearchResultJson._
87+
val jsonArr = res.parseJson.asInstanceOf[JsArray].elements
88+
val retrieveResults = jsonArr.map(r => r.convertTo[SearchResult]).toList
89+
onProperSearchResults(retrieveResults)
9690
}
9791

98-
if(!(config.raw || result.equals("")) || !config.csv.equals("")) {
99-
val unmarshalledFuture = Unmarshal(result).to[List[SearchResult]]
92+
def onProperSearchResults(sr: List[SearchResult]) = {
10093

101-
val processFuture = unmarshalledFuture.transform {
102-
case Success(unmarshalled) => {
103-
processResults(config, unmarshalled, took)
104-
Success(unmarshalled)
105-
}
106-
case Failure(e) => {
107-
error(config)(result)
108-
Failure(e)
94+
val capMessage = {
95+
config.limit match {
96+
case Some(limit) if (limit <= sr.size)
97+
=> s"Results may be capped by result limit set to $limit."
98+
case None if (sr.size >= 50)
99+
=> "Results may be capped by default limit of 50 returned results. Use --limit to extend the result set."
100+
case _
101+
=> ""
109102
}
110103
}
111-
}
112-
}
113104

114-
private def processResults(config: Config, results: List[SearchResult], queryRuntime: Double) = {
115-
val capMessage = {
116-
config.limit match {
117-
case Some(limit) if (limit <= results.size)
118-
=> s"Results may be capped by result limit set to $limit."
119-
case None if (results.size >= 50)
120-
=> "Results may be capped by default limit of 50 returned results. Use --limit to extend the result set."
121-
case _
122-
=> ""
123-
}
124-
}
125-
success(config)(s"Found ${results.size} item(s). $capMessage")
126-
reportResult(config)(results)
105+
success.apply(s"Found ${sr.size} item(s). $capMessage")
106+
reportResult.apply(sr)
127107

128-
information(config)(f"Query took $queryRuntime%.2fs.")
108+
information.apply(f"Query took ${queryRuntime.toUnit(TimeUnit.SECONDS)}%.2fs.")
129109

130-
if(!config.csv.equals("")) {
131-
exportResult(config)(results)
132-
information(config)("Results written to file '" + config.csv + "'")
110+
if (!config.csv.equals("")) {
111+
exportResult.apply(sr)
112+
information.apply("Results written to file '" + config.csv + "'")
113+
}
133114
}
134115
}
135116

136-
case class Query(query: String,
137-
limit: Option[Int] = None)
138-
139117
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (C) 2018 The Delphi Team.
2+
// See the LICENCE file distributed with this work for additional
3+
// information regarding copyright ownership.
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
package de.upb.cs.swt.delphi.cli
17+
18+
import spray.json._
19+
20+
package object commands extends DefaultJsonProtocol {
21+
22+
23+
case class Query(query: String,
24+
limit: Option[Int] = None)
25+
26+
implicit val queryFormat = jsonFormat2(Query)
27+
28+
}

0 commit comments

Comments
 (0)