Add and handle GetSetting

This commit is contained in:
Dale Wijnand 2017-01-20 16:35:06 +00:00
parent 164b0fe830
commit d9d741851a
No known key found for this signature in database
GPG Key ID: 4F256E3D151DF5EF
9 changed files with 258 additions and 7 deletions

View File

@ -7,8 +7,9 @@ package server
import java.net.{ Socket, SocketTimeoutException } import java.net.{ Socket, SocketTimeoutException }
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import sbt.protocol.{ Serialization, CommandMessage, ExecCommand, EventMessage } import scala.util.{ Left, Right }
import sjsonnew.JsonFormat import sbt.protocol._
import sjsonnew._, LList.:*:
final class NetworkChannel(val name: String, connection: Socket, state: State) extends CommandChannel { final class NetworkChannel(val name: String, connection: Socket, state: State) extends CommandChannel {
private val running = new AtomicBoolean(true) private val running = new AtomicBoolean(true)
@ -71,15 +72,136 @@ final class NetworkChannel(val name: String, connection: Socket, state: State) e
} }
def onCommand(command: CommandMessage): Unit = command match { def onCommand(command: CommandMessage): Unit = command match {
case x: ExecCommand => onExecCommand(x) case x: ExecCommand => onExecCommand(x)
case x: SettingQuery => onSettingQuery(x)
} }
private def onExecCommand(cmd: ExecCommand) = private def onExecCommand(cmd: ExecCommand) =
append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name)))) append(Exec(cmd.commandLine, cmd.execId orElse Some(Exec.newExecId), Some(CommandSource(name))))
private def onSettingQuery(req: SettingQuery) = {
import sbt.internal.util.complete.Parser
val extracted = Project extract state
val keys = Parser.parse(req.setting, Act aggregatedKeyParser extracted)
def getSettingValue[A](key: Def.ScopedKey[A]) =
extracted.structure.data.get(key.scope, key.key)
.toRight(s"Key ${Def displayFull key} not found")
.flatMap {
case _: Task[_] => Left(s"Key ${Def displayFull key} is a task, can only query settings")
case _: InputTask[_] => Left(s"Key ${Def displayFull key} is an input task, can only query settings")
case x => Right(x)
}
def zeroValues: Either[Vector[String], Vector[Any]] = Right(Vector.empty)
def anyLeftsOrAllRights[A, B](acc: Either[Vector[A], Vector[B]], elem: Either[A, B]): Either[Vector[A], Vector[B]] =
(acc, elem) match {
case (Right(a), Right(x)) => Right(a :+ x)
case (Right(_), Left(x)) => Left(Vector(x))
case (Left(a), Right(_)) => Left(a)
case (Left(a), Left(x)) => Left(a :+ x)
}
val values = keys match {
case Left(msg) => Left(s"Invalid programmatic input:" +: (msg.lines.toVector map (" " + _)))
case Right(keys) => keys.map(getSettingValue(_)).foldLeft(zeroValues)(anyLeftsOrAllRights)
}
val jsonValues = values match {
case Left(errors) => errors
case Right(values) => values map (_.toString)
}
StandardMain.exchange publishEventMessage SettingQueryResponse(jsonValues)
}
def shutdown(): Unit = { def shutdown(): Unit = {
println("Shutting down client connection") println("Shutting down client connection")
running.set(false) running.set(false)
out.close() out.close()
} }
} }
trait SettingQueryInstances {
import BasicJsonProtocol._
type SettingQueryRepr = String :*: LNil
implicit def settingQueryIso: IsoLList.Aux[SettingQuery, SettingQueryRepr] = LList.iso(
(x => "settingKey" -> x.setting :*: LNil),
(x => SettingQuery(x.head))
)
import sbt.internal.util._
type AttrKeyRepr[A] = String :*: Manifest[A] :*: Option[String] :*: Vector[AttributeKey[_]] :*: Boolean :*: Int :*: LNil
// FIXME: Can't go this IsoLList way because AttributeKey depends on AttributeKey (extend)
implicit def attrKeyIso[A]: IsoLList.Aux[AttributeKey[A], AttrKeyRepr[A]] = ???
// LList.iso[AttributeKey[A], AttrKeyRepr[A]](attrKeyToRepr, attrKeyFromRepr)
// def attrKeyToRepr[A](x: AttributeKey[A]): AttrKeyRepr[A] = (
// /* */ "label" -> x.label /* */ :*:
// /**/ "manifest" -> x.manifest /* */ :*:
// /* */ "desc" -> x.description /* */ :*:
// /* */ "extend" -> x.extend.toVector /**/ :*:
// /* */ "isLocal" -> x.isLocal /* */ :*:
// /* */ "rank" -> x.rank /* */ :*:
// LNil
// )
def attrKeyFromRepr[A](x: AttrKeyRepr[A]): AttributeKey[A] = {
val LCons("label", label,
LCons("manifest", manifest,
LCons("desc", desc,
LCons("extend", extend,
LCons("isLabel", isLocal,
LCons("rank", rank, LNil)
))))) = x
if (isLocal) AttributeKey.local[A](manifest)
else desc match {
case Some(desc) => AttributeKey(label, desc, extend, rank)(manifest)
case None => extend match {
case Seq() => AttributeKey(label, rank)(manifest)
case _ =>
// With the given API it's not possible to create an AttributeKey
// which extends other attribute keys without having a description
// But that's not enforced in the data types. So default description to ""
AttributeKey(label, "", extend, rank)(manifest)
}
}
}
// TODO: or use AttributeKey label? (String)
// implicit def attrMapFormat: JsonFormat[AttributeMap] = project[AttributeMap, Map[AttributeKey[_], Any]](
// attrMap => attrMap.entries.iterator.map(x => x.key -> x.value).toMap,
// map => AttributeMap(map.iterator.map { case (k: AttributeKey[kt], v) => AttributeEntry(k, v.asInstanceOf[kt]) }.toSeq)
// )
implicit def scopeAxisIso[A](implicit z: JsonFormat[A]): JsonFormat[ScopeAxis[A]] =
new JsonFormat[ScopeAxis[A]] {
def write[J](obj: ScopeAxis[A], builder: Builder[J]): Unit = obj match {
case This => builder writeString "This"
case Global => builder writeString "Global"
case Select(s) => z.write(s, builder)
}
def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): ScopeAxis[A] = jsOpt match {
case None => deserializationError("Expected some JSON but found None")
case Some("This") => This
case Some("Global") => Global
case Some(_) => Select(z.read(jsOpt, unbuilder))
}
}
type ScopeRepr = ScopeAxis[Reference] :*: ScopeAxis[ConfigKey] :*: ScopeAxis[AttributeKey[_]] :*: ScopeAxis[AttributeMap] :*: LNil
// implicit def scopeIso: IsoLList.Aux[Scope, ScopeRepr] = LList.iso[Scope, ScopeRepr](
// { x: Scope => "project" -> x.project :*: "config" -> x.config :*: "task" -> x.task :*: "extra" -> x.extra :*: LNil },
// { x: ScopeRepr => Scope(x.head, x.tail.head, x.tail.tail.head, x.tail.tail.tail.head) }
// )
type SettingKeyRepr[A] = Scope :*: AttributeKey[A] :*: LNil
// implicit def settingKeyIso[A]: IsoLList.Aux[SettingKey[A], SettingKeyRepr[A]] = LList.iso(
// { x: SettingKey[A] => "scope" -> x.scope :*: "attrKey" -> x.key :*: LNil },
// { x: SettingKeyRepr[A] => Scoped.scopedSetting(x.head, x.tail.head) }
// )
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class SettingQuery private (
val setting: String) extends sbt.protocol.CommandMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: SettingQuery => (this.setting == x.setting)
case _ => false
}
override def hashCode: Int = {
37 * (17 + setting.##)
}
override def toString: String = {
"SettingQuery(" + setting + ")"
}
protected[this] def copy(setting: String = setting): SettingQuery = {
new SettingQuery(setting)
}
def withSetting(setting: String): SettingQuery = {
copy(setting = setting)
}
}
object SettingQuery {
def apply(setting: String): SettingQuery = new SettingQuery(setting)
}

View File

@ -0,0 +1,32 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol
final class SettingQueryResponse private (
val values: Vector[String]) extends sbt.protocol.EventMessage() with Serializable {
override def equals(o: Any): Boolean = o match {
case x: SettingQueryResponse => (this.values == x.values)
case _ => false
}
override def hashCode: Int = {
37 * (17 + values.##)
}
override def toString: String = {
"SettingQueryResponse(" + values + ")"
}
protected[this] def copy(values: Vector[String] = values): SettingQueryResponse = {
new SettingQueryResponse(values)
}
def withValues(values: Vector[String]): SettingQueryResponse = {
copy(values = values)
}
}
object SettingQueryResponse {
def apply(values: Vector[String]): SettingQueryResponse = new SettingQueryResponse(values)
}

View File

@ -5,6 +5,6 @@
// DO NOT EDIT MANUALLY // DO NOT EDIT MANUALLY
package sbt.protocol.codec package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats => trait CommandMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.SettingQueryFormats =>
implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat1[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand]("type") implicit lazy val CommandMessageFormat: JsonFormat[sbt.protocol.CommandMessage] = flatUnionFormat2[sbt.protocol.CommandMessage, sbt.protocol.ExecCommand, sbt.protocol.SettingQuery]("type")
} }

View File

@ -5,6 +5,6 @@
// DO NOT EDIT MANUALLY // DO NOT EDIT MANUALLY
package sbt.protocol.codec package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder } import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats => trait EventMessageFormats { self: sjsonnew.BasicJsonProtocol with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.ExecStatusEventFormats with sbt.protocol.codec.SettingQueryResponseFormats =>
implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat3[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent]("type") implicit lazy val EventMessageFormat: JsonFormat[sbt.protocol.EventMessage] = flatUnionFormat4[sbt.protocol.EventMessage, sbt.protocol.ChannelAcceptedEvent, sbt.protocol.LogEvent, sbt.protocol.ExecStatusEvent, sbt.protocol.SettingQueryResponse]("type")
} }

View File

@ -6,10 +6,12 @@
package sbt.protocol.codec package sbt.protocol.codec
trait JsonProtocol extends sjsonnew.BasicJsonProtocol trait JsonProtocol extends sjsonnew.BasicJsonProtocol
with sbt.protocol.codec.ExecCommandFormats with sbt.protocol.codec.ExecCommandFormats
with sbt.protocol.codec.SettingQueryFormats
with sbt.protocol.codec.CommandMessageFormats with sbt.protocol.codec.CommandMessageFormats
with sbt.protocol.codec.ChannelAcceptedEventFormats with sbt.protocol.codec.ChannelAcceptedEventFormats
with sbt.protocol.codec.LogEventFormats with sbt.protocol.codec.LogEventFormats
with sbt.protocol.codec.ExecStatusEventFormats with sbt.protocol.codec.ExecStatusEventFormats
with sbt.protocol.codec.SettingQueryResponseFormats
with sbt.protocol.codec.EventMessageFormats with sbt.protocol.codec.EventMessageFormats
with sbt.protocol.codec.ExecutionEventFormats with sbt.protocol.codec.ExecutionEventFormats
object JsonProtocol extends JsonProtocol object JsonProtocol extends JsonProtocol

View File

@ -0,0 +1,27 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait SettingQueryFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val SettingQueryFormat: JsonFormat[sbt.protocol.SettingQuery] = new JsonFormat[sbt.protocol.SettingQuery] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQuery = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val setting = unbuilder.readField[String]("setting")
unbuilder.endObject()
sbt.protocol.SettingQuery(setting)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.SettingQuery, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("setting", obj.setting)
builder.endObject()
}
}
}

View File

@ -0,0 +1,27 @@
/**
* This code is generated using sbt-datatype.
*/
// DO NOT EDIT MANUALLY
package sbt.protocol.codec
import _root_.sjsonnew.{ deserializationError, serializationError, Builder, JsonFormat, Unbuilder }
trait SettingQueryResponseFormats { self: sjsonnew.BasicJsonProtocol =>
implicit lazy val SettingQueryResponseFormat: JsonFormat[sbt.protocol.SettingQueryResponse] = new JsonFormat[sbt.protocol.SettingQueryResponse] {
override def read[J](jsOpt: Option[J], unbuilder: Unbuilder[J]): sbt.protocol.SettingQueryResponse = {
jsOpt match {
case Some(js) =>
unbuilder.beginObject(js)
val values = unbuilder.readField[Vector[String]]("values")
unbuilder.endObject()
sbt.protocol.SettingQueryResponse(values)
case None =>
deserializationError("Expected JsObject but found None")
}
}
override def write[J](obj: sbt.protocol.SettingQueryResponse, builder: Builder[J]): Unit = {
builder.beginObject()
builder.addField("values", obj.values)
builder.endObject()
}
}
}

View File

@ -13,6 +13,11 @@ type ExecCommand implements CommandMessage {
execId: String @since("0.0.1") execId: String @since("0.0.1")
} }
type SettingQuery implements CommandMessage {
setting: String!
}
## Message for events. ## Message for events.
interface EventMessage { interface EventMessage {
} }
@ -35,6 +40,10 @@ type ExecStatusEvent implements EventMessage {
commandQueue: [String] commandQueue: [String]
} }
type SettingQueryResponse implements EventMessage {
values: [String]
}
# enum Status { # enum Status {
# Ready # Ready
# Processing # Processing