Skip to content

Commit 0a7b5c7

Browse files
author
Johannes Düsing
committed
Enable users to store POMs, JARs or both when using the search command
1 parent f334e24 commit 0a7b5c7

File tree

6 files changed

+177
-4
lines changed

6 files changed

+177
-4
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

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

19+
import de.upb.cs.swt.delphi.cli.OutputMode.OutputMode
20+
1921
/**
2022
* Represents a configuration for the Delphi CLI
2123
*
@@ -27,6 +29,8 @@ case class Config(server: String = sys.env.getOrElse("DELPHI_SERVER", "https://d
2729
verbose: Boolean = false,
2830
raw: Boolean = false,
2931
csv: String = "",
32+
output: String = "",
33+
outputMode: Option[OutputMode] = None,
3034
silent: Boolean = false,
3135
list : Boolean = false,
3236
mode: String = "",
@@ -41,3 +45,15 @@ case class Config(server: String = sys.env.getOrElse("DELPHI_SERVER", "https://d
4145
lazy val csvOutput = new CsvOutput(this)
4246

4347
}
48+
49+
object OutputMode extends Enumeration {
50+
type OutputMode = Value
51+
val JarOnly, PomOnly, All = Value
52+
53+
def fromString(value:String): Option[OutputMode] = value.toLowerCase match {
54+
case "jaronly" => Some(JarOnly)
55+
case "pomonly" => Some(PomOnly)
56+
case "all" => Some(All)
57+
case _ => None
58+
}
59+
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

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

19+
import java.nio.file.{Files, Paths}
20+
1921
import com.softwaremill.sttp._
2022
import de.upb.cs.swt.delphi.cli.commands._
2123

@@ -99,7 +101,18 @@ object DelphiCLI {
99101
opt[String]("csv").action((x, c) => c.copy(csv = x)).text("Path to the output .csv file (overwrites existing file)"),
100102
opt[Int]("limit").action((x, c) => c.copy(limit = Some(x))).text("The maximal number of results returned."),
101103
opt[Unit](name = "list").action((_, c) => c.copy(list = true)).text("Output results as list (raw option overrides this)"),
102-
opt[Int]("timeout").action((x, c) => c.copy(timeout = Some(x))).text("Timeout in seconds.")
104+
opt[Int]("timeout").action((x, c) => c.copy(timeout = Some(x))).text("Timeout in seconds."),
105+
opt[String](name = "output")
106+
.validate(x => if (Files.isDirectory(Paths.get(x))) success else failure(f"Output directory not found at $x"))
107+
.action((x, c) => c.copy(output = x))
108+
.text("Directory to write the search results to"),
109+
opt[String](name = "outputmode")
110+
.validate(x => OutputMode.fromString(x) match {
111+
case Some(_) => success
112+
case None => failure("Only JarOnly, PomOnly and All are supported for output modes.")
113+
})
114+
.action((x, c) => c.copy(outputMode = OutputMode.fromString(x)))
115+
.text("Defines what to store. Supported are JarOnly, PomOnly and All. Defaults to PomOnly. Requires output to be set.")
103116
)
104117
}
105118
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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 java.io.{BufferedWriter, FileOutputStream, FileWriter}
19+
import java.nio.file.{Files, Paths}
20+
21+
import com.softwaremill.sttp._
22+
import de.upb.cs.swt.delphi.cli.artifacts.{QueryStorageMetadata, SearchResult}
23+
import org.joda.time.DateTime
24+
import org.joda.time.format.DateTimeFormat
25+
import spray.json._
26+
27+
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultJson.searchResultFormat
28+
import de.upb.cs.swt.delphi.cli.artifacts.StorageMetadataJson.queryStorageMetadataFormat
29+
30+
class FileOutput (serverVersion: String = "UNKNOWN")(implicit config:Config, backend: SttpBackend[Id, Nothing]){
31+
32+
def writeSearchResults(results: List[SearchResult]) : Unit = {
33+
val metadata = buildQueryMetadata(results)
34+
35+
val folderPath = Paths.get(config.output, DateTimeFormat.forPattern("YYYY-MM-dd_HH_mm_ss").print(metadata.timestamp))
36+
Files.createDirectory(folderPath)
37+
38+
val metadataPath = Paths.get(folderPath.toString, "query-metadata.json").toString
39+
val writer = new BufferedWriter(new FileWriter(metadataPath))
40+
writer.write(metadata.toJson.prettyPrint)
41+
writer.close()
42+
43+
val outputMode = config.outputMode.getOrElse(OutputMode.PomOnly)
44+
45+
info()
46+
info(f"Output Mode is ${outputMode.toString}, destination is ${folderPath.toString}")
47+
48+
results
49+
.map(r => r.toMavenRelativeUrl() + s"/${r.metadata.artifactId}-${r.metadata.version}")
50+
.map(relUrl => "https://repo1.maven.org/maven2/" + relUrl).foreach( urlWithoutExtension => {
51+
52+
var artifactsToRetrieve = Seq[String]()
53+
if (outputMode == OutputMode.PomOnly || outputMode == OutputMode.All){
54+
artifactsToRetrieve = artifactsToRetrieve ++ Seq(s"$urlWithoutExtension.pom")
55+
}
56+
if(outputMode == OutputMode.JarOnly || outputMode == OutputMode.All){
57+
artifactsToRetrieve = artifactsToRetrieve ++ Seq(s"$urlWithoutExtension.jar")
58+
}
59+
60+
artifactsToRetrieve.foreach( url => {
61+
sttp.get(uri"$url").response(asByteArray).send().body match {
62+
case Right(value) =>
63+
new FileOutputStream(Paths.get(folderPath.toString, url.splitAt(url.lastIndexOf('/'))._2).toString)
64+
.write(value)
65+
case Left(value) =>
66+
error(f"Failed to download artifact from $url, got: $value")
67+
}
68+
})
69+
70+
})
71+
72+
info(f"Successfully wrote results to $folderPath.")
73+
}
74+
75+
private def info(value: String = ""):Unit = config.consoleOutput.outputInformation(value)
76+
private def error(value: String = ""):Unit = config.consoleOutput.outputError(value)
77+
78+
79+
private def buildQueryMetadata(results: List[SearchResult]) =
80+
QueryStorageMetadata( query = config.query,
81+
results = results,
82+
serverVersion = serverVersion,
83+
serverUrl = config.server,
84+
clientVersion = BuildInfo.version,
85+
timestamp = DateTime.now(),
86+
resultLimit = config.limit.getOrElse(50),
87+
outputMode = config.outputMode.getOrElse(OutputMode.PomOnly).toString
88+
)
89+
}

src/main/scala/de/upb/cs/swt/delphi/cli/artifacts/SearchResult.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ trait Result{
2525

2626
def toMavenIdentifier() : String = s"${metadata.groupId}:${metadata.artifactId}:${metadata.version}"
2727

28+
def toMavenRelativeUrl(): String =
29+
s"${metadata.groupId.replace(".", "/")}/${metadata.artifactId}/${metadata.version}"
30+
2831
def fieldNames() : List[String] = metricResults.keys.toList.sorted
2932
}
3033

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
17+
package de.upb.cs.swt.delphi.cli.artifacts
18+
19+
import org.joda.time.DateTime
20+
import spray.json.{DefaultJsonProtocol, JsonFormat}
21+
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultJson.searchResultFormat
22+
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultsJson.DateJsonFormat
23+
24+
trait StorageMetadata {
25+
val clientVersion: String
26+
val serverVersion: String
27+
val serverUrl: String
28+
val timestamp: DateTime
29+
val outputMode: String
30+
}
31+
32+
case class QueryStorageMetadata(query: String,
33+
results: Seq[SearchResult],
34+
resultLimit: Int,
35+
clientVersion: String,
36+
serverVersion: String,
37+
serverUrl: String,
38+
outputMode: String,
39+
timestamp: DateTime) extends StorageMetadata
40+
41+
object StorageMetadataJson extends DefaultJsonProtocol {
42+
implicit val queryStorageMetadataFormat: JsonFormat[QueryStorageMetadata] = jsonFormat8(QueryStorageMetadata)
43+
}

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ import java.util.concurrent.TimeUnit
2020

2121
import com.softwaremill.sttp._
2222
import com.softwaremill.sttp.sprayJson._
23-
import de.upb.cs.swt.delphi.cli.Config
24-
import de.upb.cs.swt.delphi.cli.artifacts.{SearchResult, SearchResults}
23+
import de.upb.cs.swt.delphi.cli.{Config, FileOutput}
24+
import de.upb.cs.swt.delphi.cli.artifacts.SearchResults
2525
import de.upb.cs.swt.delphi.cli.artifacts.SearchResultsJson._
2626
import spray.json._
2727

@@ -75,7 +75,8 @@ object SearchCommand extends Command with DefaultJsonProtocol{
7575
(resStr, took)
7676
}
7777

78-
private def processResults(res: String, queryRuntime: FiniteDuration)(implicit config: Config) = {
78+
private def processResults(res: String, queryRuntime: FiniteDuration)
79+
(implicit config: Config, backend: SttpBackend[Id, Nothing]) = {
7980

8081
if (config.raw || res.equals("")) {
8182
reportResult.apply(res)
@@ -103,11 +104,19 @@ object SearchCommand extends Command with DefaultJsonProtocol{
103104

104105
information.apply(f"Query roundtrip took ${queryRuntime.toUnit(TimeUnit.MILLISECONDS)}%.0fms.")
105106

107+
if (!config.output.equals("")){
108+
val output = new FileOutput(executeGet(Seq("version")).getOrElse("UNKNOWN"))
109+
output.writeSearchResults(sr)
110+
}
111+
106112
if (!config.csv.equals("")) {
107113
exportResult.apply(sr)
108114
information.apply("Results written to file '" + config.csv + "'")
109115
}
110116
}
111117
}
112118

119+
120+
121+
113122
}

0 commit comments

Comments
 (0)