-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
28 changed files
with
906 additions
and
314 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,6 @@ | |
/.idea/ | ||
/.bsp/ | ||
/project/target/ | ||
/target/ | ||
target/ | ||
/esw-segment-db/target/ | ||
/esw-segment-db/xxx.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
version = 2.6.4 | ||
|
||
project.excludeFilters = ["BuildInfo.scala", "target/"] | ||
|
||
align.preset = more | ||
|
||
docstrings = JavaDoc | ||
indentOperator.preset = spray | ||
maxColumn = 130 | ||
newlines.alwaysBeforeElseAfterCurlyIf = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
esw-segment-client/src/main/scala/esw/segment/client/EswSegmentClientOptions.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package esw.segment.client | ||
|
||
import java.util.Date | ||
|
||
object EswSegmentClientOptions { | ||
val defaultPort = 9192 | ||
} | ||
|
||
case class EswSegmentClientOptions( | ||
host: String = "localhost", | ||
port: Int = EswSegmentClientOptions.defaultPort, | ||
date: Date = new Date(), | ||
from: Date = new Date(), | ||
to: Date = new Date(), | ||
segmentId: Option[String] = None, | ||
position: Option[Int] = None, | ||
setPosition: Option[Unit] = None, | ||
segmentPositions: Option[Unit] = None, | ||
segmentIds: Option[Unit] = None, | ||
newlyInstalledSegments: Option[Unit] = None, | ||
currentPositions: Option[Unit] = None, | ||
currentSegmentPosition: Option[Unit] = None, | ||
currentSegmentAtPosition: Option[Unit] = None, | ||
positionsOnDate: Option[Unit] = None, | ||
segmentPositionOnDate: Option[Unit] = None, | ||
segmentAtPositionOnDate: Option[Unit] = None | ||
) |
180 changes: 180 additions & 0 deletions
180
esw-segment-client/src/main/scala/esw/segment/client/EswSegmentDbClient.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package esw.segment.client | ||
|
||
import java.util.Date | ||
|
||
import akka.actor.ActorSystem | ||
import buildinfo.BuildInfo | ||
import esw.segment.shared.EswSegmentData._ | ||
import EswSegmentClientOptions._ | ||
import scopt.Read.reads | ||
import scopt.Read | ||
|
||
import scala.async.Async.{async, await} | ||
import scala.concurrent.{Await, Future} | ||
import scala.concurrent.duration._ | ||
|
||
object EswSegmentDbClient extends App { | ||
val dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd") | ||
implicit val system: ActorSystem = ActorSystem() | ||
|
||
import system._ | ||
|
||
implicit val durationRead: Read[Date] = | ||
reads { | ||
dateFormat.parse | ||
} | ||
|
||
// Parser for the command line options | ||
private val parser = new scopt.OptionParser[EswSegmentClientOptions]("esw-segment-db-client") { | ||
head("esw-segment-db-client", BuildInfo.version) | ||
|
||
opt[String]("host") valueName "<hostname>" action { (x, c) => | ||
c.copy(host = x) | ||
} text s"The host name where the ESW Segment DB HTTP server is running (default: localhost)" | ||
|
||
opt[Int]("port") valueName "<number>" action { (x, c) => | ||
c.copy(port = x) | ||
} text s"The port number to use for the server (default: $defaultPort)" | ||
|
||
opt[Date]('d', "date") valueName dateFormat.toPattern action { (x, c) => | ||
c.copy(date = x) | ||
} text s"The date to use (default: current date)" | ||
|
||
opt[Date]("from") valueName dateFormat.toPattern action { (x, c) => | ||
c.copy(from = x) | ||
} text s"The starting date to use for a date range (default: current date)" | ||
|
||
opt[Date]("to") valueName dateFormat.toPattern action { (x, c) => | ||
c.copy(to = x) | ||
} text s"The ending date to use for a date range (default: current date)" | ||
|
||
opt[String]('s', "segmentId") valueName "<id>" action { (x, c) => | ||
c.copy(segmentId = Some(x)) | ||
} text s"The segment id to use" | ||
|
||
opt[Int]('p', "position") valueName "<number>" action { (x, c) => | ||
c.copy(position = Some(x)) | ||
} text s"The segment position to use (number in range 1 to 492)" | ||
|
||
opt[Unit]("setPosition") action { (_, c) => | ||
c.copy(setPosition = Some(())) | ||
} text s"Sets or updates the date and position of the given segment (Requires --position, --segmentId if segment is present)" | ||
|
||
opt[Unit]("segmentPositions") action { (_, c) => | ||
c.copy(segmentPositions = Some(())) | ||
} text s"Gets a list of segments positions for the given segment id in the given date range (Requires --segmentId)" | ||
|
||
opt[Unit]("segmentIds") action { (_, c) => | ||
c.copy(segmentIds = Some(())) | ||
} text s"Gets a list of segment ids that were in the given position in the given date range (Requires --position)" | ||
|
||
opt[Unit]("newlyInstalledSegments") action { (_, c) => | ||
c.copy(newlyInstalledSegments = Some(())) | ||
} text s"Gets a list of segments that were installed since the given date (Requires --date)" | ||
|
||
opt[Unit]("currentPositions") action { (_, c) => | ||
c.copy(currentPositions = Some(())) | ||
} text s"Gets the current segment positions, sorted by position" | ||
|
||
opt[Unit]("currentSegmentPosition") action { (_, c) => | ||
c.copy(currentSegmentPosition = Some(())) | ||
} text s"Gets the current segment position for the given segment id (Requires --segmentId)" | ||
|
||
opt[Unit]("currentSegmentAtPosition") action { (_, c) => | ||
c.copy(currentSegmentAtPosition = Some(())) | ||
} text s"Gets the id of the segment currently in the given position (Requires --position)" | ||
|
||
opt[Unit]("positionsOnDate") action { (_, c) => | ||
c.copy(positionsOnDate = Some(())) | ||
} text s"Gets the segment positions as they were on the given date, sorted by position" | ||
|
||
opt[Unit]("segmentPositionOnDate") action { (_, c) => | ||
c.copy(segmentPositionOnDate = Some(())) | ||
} text s"Gets the segment position for the given segment id on the given date (Requires --segmentId)" | ||
|
||
opt[Unit]("segmentAtPositionOnDate") action { (_, c) => | ||
c.copy(segmentAtPositionOnDate = Some(())) | ||
} text s"Gets the id of the segment that was installed in the given position on the given date (Requires --position)" | ||
|
||
} | ||
|
||
// Parse the command line options | ||
parser.parse(args, EswSegmentClientOptions()) match { | ||
case Some(options) => | ||
try { | ||
Await.ready(run(options), 60.seconds) | ||
} | ||
catch { | ||
case e: Throwable => | ||
e.printStackTrace() | ||
System.exit(1) | ||
} | ||
case None => System.exit(1) | ||
} | ||
|
||
private def error(msg: String): Unit = { | ||
println(msg) | ||
System.exit(1) | ||
} | ||
|
||
private def showResults(result: List[SegmentToM1Pos]): Unit = { | ||
result.foreach(r => println(s"${dateFormat.format(r.date)} ${r.maybeId.getOrElse("------")} ${r.pos}")) | ||
} | ||
|
||
// Run the application | ||
private def run(options: EswSegmentClientOptions): Future[Unit] = | ||
async { | ||
import options._ | ||
val client = new EswSegmentHttpClient(host, port) | ||
|
||
if (options.setPosition.isDefined) { | ||
if (position.isEmpty) error("--position option is required") | ||
val segmentToM1Pos = SegmentToM1Pos(date, segmentId, position.get) | ||
val result = await(client.setPosition(segmentToM1Pos)) | ||
if (!result) | ||
error(s"setPosition failed for date: $date, segmentId: ${segmentId.getOrElse("None")}, position: ${position.get}") | ||
} | ||
|
||
if (options.segmentPositions.isDefined) { | ||
if (segmentId.isEmpty) error("--segmentId option is required") | ||
showResults(await(client.segmentPositions(DateRange(from, to), segmentId.get))) | ||
} | ||
|
||
if (options.segmentIds.isDefined) { | ||
if (position.isEmpty) error("--position option is required") | ||
showResults(await(client.segmentIds(DateRange(from, to), position.get))) | ||
} | ||
|
||
if (options.newlyInstalledSegments.isDefined) { | ||
showResults(await(client.newlyInstalledSegments(date))) | ||
} | ||
|
||
if (options.currentPositions.isDefined) { | ||
showResults(await(client.currentPositions())) | ||
} | ||
|
||
if (options.currentSegmentPosition.isDefined) { | ||
if (segmentId.isEmpty) error("--segmentId option is required") | ||
showResults(await(client.currentSegmentPosition(segmentId.get)).toList) | ||
} | ||
|
||
if (options.currentSegmentAtPosition.isDefined) { | ||
if (position.isEmpty) error("--position option is required") | ||
showResults(await(client.currentSegmentAtPosition(position.get)).toList) | ||
} | ||
|
||
if (options.positionsOnDate.isDefined) { | ||
showResults(await(client.positionsOnDate(date))) | ||
} | ||
|
||
if (options.segmentPositionOnDate.isDefined) { | ||
if (segmentId.isEmpty) error("--segmentId option is required") | ||
showResults(await(client.segmentPositionOnDate(date, segmentId.get)).toList) | ||
} | ||
|
||
if (options.segmentAtPositionOnDate.isDefined) { | ||
if (position.isEmpty) error("--position option is required") | ||
showResults(await(client.segmentAtPositionOnDate(date, position.get)).toList) | ||
} | ||
} | ||
} |
124 changes: 124 additions & 0 deletions
124
esw-segment-client/src/main/scala/esw/segment/client/EswSegmentHttpClient.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package esw.segment.client | ||
|
||
import java.sql.Date | ||
|
||
import esw.segment.shared.{EswSegmentData, JsonSupport, SegmentToM1Api} | ||
import EswSegmentData._ | ||
import akka.actor.ActorSystem | ||
import akka.http.scaladsl.Http | ||
import akka.http.scaladsl.model.StatusCodes.OK | ||
import akka.http.scaladsl.model.{ContentTypes, HttpEntity, HttpMethods, HttpRequest, Uri} | ||
import spray.json._ | ||
import akka.http.scaladsl.unmarshalling.Unmarshal | ||
import EswSegmentClientOptions._ | ||
|
||
import scala.async.Async.{async, await} | ||
import scala.concurrent.{ExecutionContextExecutor, Future} | ||
|
||
/** | ||
* HTTP Client for ESW Segment DB HTTP Server | ||
*/ | ||
class EswSegmentHttpClient(host: String = "localhost", port: Int = defaultPort)(implicit | ||
actorSystem: ActorSystem, | ||
ec: ExecutionContextExecutor | ||
) extends SegmentToM1Api | ||
with JsonSupport { | ||
|
||
private val baseUri = s"http://$host:$port" | ||
|
||
private def postSet(uri: Uri, json: String): Future[Boolean] = | ||
async { | ||
val entity = HttpEntity(ContentTypes.`application/json`, json) | ||
val request = HttpRequest(HttpMethods.POST, uri = uri, entity = entity) | ||
val response = await(Http().singleRequest(request)) | ||
response.status == OK | ||
} | ||
|
||
private def postGetList(uri: Uri, json: String): Future[List[SegmentToM1Pos]] = | ||
async { | ||
val entity = HttpEntity(ContentTypes.`application/json`, json) | ||
val request = HttpRequest(HttpMethods.POST, uri = uri, entity = entity) | ||
val response = await(Http().singleRequest(request)) | ||
await(Unmarshal(response).to[List[SegmentToM1Pos]]) | ||
} | ||
|
||
private def postGetOption(uri: Uri, json: String): Future[Option[SegmentToM1Pos]] = | ||
async { | ||
val entity = HttpEntity(ContentTypes.`application/json`, json) | ||
val request = HttpRequest(HttpMethods.POST, uri = uri, entity = entity) | ||
val response = await(Http().singleRequest(request)) | ||
await(Unmarshal(response).to[Option[SegmentToM1Pos]]) | ||
} | ||
|
||
private def getOption(uri: Uri): Future[Option[SegmentToM1Pos]] = | ||
async { | ||
val request = HttpRequest(HttpMethods.GET, uri = uri) | ||
val response = await(Http().singleRequest(request)) | ||
await(Unmarshal(response).to[Option[SegmentToM1Pos]]) | ||
} | ||
|
||
// -- | ||
|
||
override def setPosition(segmentToM1Pos: SegmentToM1Pos): Future[Boolean] = { | ||
postSet(Uri(s"$baseUri/setPosition"), segmentToM1Pos.toJson.compactPrint) | ||
} | ||
|
||
override def setPositions(date: Date, positions: List[(Option[String], Int)]): Future[Boolean] = { | ||
postSet(Uri(s"$baseUri/setPositions"), SegmentToM1Positions(date, positions).toJson.compactPrint) | ||
} | ||
|
||
override def setAllPositions(date: Date, allPositions: List[Option[String]]): Future[Boolean] = { | ||
postSet(Uri(s"$baseUri/setAllPositions"), AllSegmentPositions(date, allPositions).toJson.compactPrint) | ||
} | ||
|
||
override def segmentPositions(dateRange: DateRange, segmentId: String): Future[List[SegmentToM1Pos]] = { | ||
postGetList(Uri(s"$baseUri/segmentPositions/$segmentId"), dateRange.toJson.compactPrint) | ||
} | ||
|
||
override def segmentIds(dateRange: DateRange, pos: Int): Future[List[SegmentToM1Pos]] = { | ||
postGetList(Uri(s"$baseUri/segmentIds/$pos"), dateRange.toJson.compactPrint) | ||
} | ||
|
||
override def newlyInstalledSegments(since: Date): Future[List[SegmentToM1Pos]] = { | ||
postGetList(Uri(s"$baseUri/newlyInstalledSegments"), since.toJson.compactPrint) | ||
} | ||
|
||
override def positionsOnDate(date: Date): Future[List[SegmentToM1Pos]] = { | ||
postGetList(Uri(s"$baseUri/positionsOnDate"), date.toJson.compactPrint) | ||
} | ||
|
||
override def segmentPositionOnDate(date: Date, segmentId: String): Future[Option[SegmentToM1Pos]] = { | ||
postGetOption(Uri(s"$baseUri/segmentPositionOnDate/$segmentId"), date.toJson.compactPrint) | ||
} | ||
|
||
override def segmentAtPositionOnDate(date: Date, pos: Int): Future[Option[SegmentToM1Pos]] = { | ||
postGetOption(Uri(s"$baseUri/segmentAtPositionOnDate/$pos"), date.toJson.compactPrint) | ||
} | ||
|
||
override def currentPositions(): Future[List[SegmentToM1Pos]] = | ||
async { | ||
val uri = Uri(s"$baseUri/currentPositions") | ||
val request = HttpRequest(HttpMethods.GET, uri = uri) | ||
val response = await(Http().singleRequest(request)) | ||
await(Unmarshal(response).to[List[SegmentToM1Pos]]) | ||
} | ||
|
||
override def currentSegmentPosition(segmentId: String): Future[Option[SegmentToM1Pos]] = { | ||
getOption(Uri(s"$baseUri/currentSegmentPosition/$segmentId")) | ||
} | ||
|
||
override def currentSegmentAtPosition(pos: Int): Future[Option[SegmentToM1Pos]] = { | ||
getOption(Uri(s"$baseUri/currentSegmentAtPosition/$pos")) | ||
} | ||
|
||
/** | ||
* Drops and recreates the database tables (for testing) | ||
*/ | ||
override def resetTables(): Future[Boolean] = | ||
async { | ||
val uri = Uri(s"$baseUri/resetTables") | ||
val request = HttpRequest(HttpMethods.POST, uri = uri) | ||
val response = await(Http().singleRequest(request)) | ||
response.status == OK | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
esw-segment-client/src/test/scala/esw/segment/client/EswSegmentClientTest.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package esw.segment.client | ||
|
||
import akka.actor.ActorSystem | ||
import esw.segment.shared.SegmentToM1ApiTestBase | ||
import EswSegmentClientTest._ | ||
|
||
object EswSegmentClientTest { | ||
implicit val system: ActorSystem = ActorSystem() | ||
import system._ | ||
val client = new EswSegmentHttpClient() | ||
} | ||
|
||
class EswSegmentClientTest extends SegmentToM1ApiTestBase(client) |
Oops, something went wrong.