Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ script:
after_success:
- sbt coveralls
scala:
- 2.12.4
- 2.12.6
jdk:
- oraclejdk8

117 changes: 59 additions & 58 deletions client/src/main/scala/pme123/adapters/client/ClientWebsocket.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package pme123.adapters.client

import com.thoughtworks.binding.Binding.Var
import org.scalajs.dom.raw._
import org.scalajs.dom.window
import org.scalajs.dom._
import play.api.libs.json.{JsError, JsSuccess, JsValue, Json}
import pme123.adapters.shared.{AdapterMsg, RunJob, RunStarted, _}

Expand All @@ -12,68 +11,70 @@ object ClientWebsocket
extends ClientUtils {

private lazy val wsURL =
s"${window.location.protocol
.replace("http", "ws")}//${window.location.host}${UIStore.uiState.webContext.value}/ws"
s"${
window.location.protocol
.replace("http", "ws")
}//${window.location.host}${UIStore.uiState.webContext.value}/ws"

private val webSocket: Var[Option[WebSocket]] = Var(None)
private val reconnectWSCode = 3001


def connectWS(maybeWebPath: Option[String]) {
def connectWS() {
closeWS()
maybeWebPath.foreach { webPath =>
val path = s"$wsURL$webPath"
val socket = new WebSocket(path)
webSocket.value = Some(socket)
info(s"Connect to Websocket: $path")
socket.onmessage = {
e: MessageEvent =>
val message = Json.parse(e.data.toString)
message.validate[AdapterMsg] match {
case JsSuccess(AdapterRunning(logReport), _) =>
UIStore.changeIsRunning(true)
UIStore.addLogReport(logReport)
case JsSuccess(AdapterNotRunning(logReport), _) =>
UIStore.changeIsRunning(false)
logReport.foreach { lr =>
UIStore.changeLastLogLevel(lr)
UIStore.addLogReport(lr)
}
case JsSuccess(LogEntryMsg(le), _) =>
UIStore.addLogEntry(le)
case JsSuccess(RunStarted, _) =>
UIStore.changeIsRunning(true)
case JsSuccess(RunFinished(logReport), _) =>
UIStore.changeIsRunning(false)
UIStore.changeLastLogLevel(logReport)
case JsSuccess(adapterInfo: ProjectInfo, _) =>
UIStore.changeProjectInfo(adapterInfo)
case JsSuccess(ClientConfigMsg(clientConfig), _) =>
UIStore.changeSelectedClientConfig(Some(clientConfig))
case JsSuccess(GenericResult(payload, append), _) =>
UIStore.replaceLastResult(payload, append)
case JsSuccess(GenericResults(payload, append), _) =>
UIStore.replaceLastResults(payload,append)
case JsSuccess(other, _) =>
info(s"Other message: $other")
case JsError(errors) =>
errors.foreach(e => error(e.toString))
}
}
socket.onerror = { e: ErrorEvent =>
error(s"exception with websocket: ${e.message}!")
socket.close(0, e.message)
}
socket.onopen = { _: Event =>
info("websocket open!")
UIStore.clearLogData()
}
socket.onclose = { e: CloseEvent =>
info("closed socket" + e.reason)
if (e.code != reconnectWSCode) {
setTimeout(1000) {
connectWS(maybeWebPath) // try to reconnect automatically
}
val webPath = UIStore.uiState.webPath.value
val path = s"$wsURL$webPath"
val socket = new WebSocket(path)
webSocket.value = Some(socket)
info(s"Connect to Websocket: $path")
socket.onmessage = {
e: MessageEvent =>
val message = Json.parse(e.data.toString)
message.validate[AdapterMsg] match {
case JsSuccess(AdapterRunning(logReport), _) =>
UIStore.changeIsRunning(true)
UIStore.addLogReport(logReport)
case JsSuccess(AdapterNotRunning(logReport), _) =>
UIStore.changeIsRunning(false)
logReport.foreach { lr =>
UIStore.changeLastLogLevel(lr)
UIStore.addLogReport(lr)
}
case JsSuccess(LogEntryMsg(le), _) =>
UIStore.addLogEntry(le)
case JsSuccess(RunStarted, _) =>
UIStore.changeIsRunning(true)
case JsSuccess(RunFinished(logReport), _) =>
UIStore.changeIsRunning(false)
UIStore.changeLastLogLevel(logReport)
case JsSuccess(adapterInfo: ProjectInfo, _) =>
UIStore.changeProjectInfo(adapterInfo)
case JsSuccess(ClientConfigMsg(clientConfig), _) =>
UIStore.changeSelectedClientConfig(Some(clientConfig))
case JsSuccess(GenericResult(payload, append), _) =>
UIStore.replaceLastResult(payload, append)
case JsSuccess(GenericResults(payload, append), _) =>
UIStore.replaceLastResults(payload, append)
case JsSuccess(other, _) =>
info(s"Other message: $other")
case JsError(errors) =>
errors.foreach(e => error(e.toString))
}
}
socket.onerror = { e: Event =>
val ee = e.asInstanceOf[ErrorEvent]
error(s"exception with websocket: ${ee.message}!")
socket.close(0, ee.message)
}
socket.onopen = { _: Event =>
info("websocket open!")
UIStore.clearLogData()
}
socket.onclose = { e: CloseEvent =>
info("closed socket" + e.reason)
if (e.code != reconnectWSCode) {
setTimeout(1000) {
connectWS() // try to reconnect automatically
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ object DefaultClient

@JSExportTopLevel("client.DefaultClient.main")
def mainPage(context: String
, websocketPath: String
, webPath: String
, clientType: String): Unit = {
info(s"DemoClient $clientType: $context$websocketPath")
info(s"DemoClient $clientType: $context$webPath")
UIStore.changeWebContext(context)
UIStore.changeWebPath(webPath)

ClientType.withNameInsensitiveOption(clientType) match {
case Some(JOB_PROCESS) =>
JobProcessView(websocketPath, DefaultRunJobDialog).create()
JobProcessView(DefaultRunJobDialog).create()
case Some(JOB_RESULTS) =>
DefaultView("there is no JobResults page defined").create()
case Some(CUSTOM_PAGE) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ private[client] object JobProcessHeader
{s"Job Cockpit: ${clientConfig.map(_.jobConfig.jobIdent).getOrElse("")}"}
</div>
}

@dom
private def lastLevel = {
val logLevel = UIStore.uiState.lastLogLevel.bind
Expand All @@ -65,7 +66,8 @@ private[client] object JobProcessHeader
<span
data:data-tooltip={"Log level last Adapter Process: " + logLevel.getOrElse("Not run!")}
data:data-position="bottom center">
{logConstants(levelClass).bind}
{Constants(levelClass.toList: _*)
.map(_.bind).bind}
</span>
</div>
}
Expand Down Expand Up @@ -116,7 +118,8 @@ private[client] object JobProcessHeader

<div class="ui item">
<button class="ui basic icon button"
onclick={_: Event => UIStore.showRunJobDialog()
onclick={_: Event =>
UIStore.showRunJobDialog()
setTimeout(200) {
jQuery(".ui.modal").modal("show")
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@ import pme123.adapters.shared.LogEntry
import scala.language.implicitConversions
import scala.scalajs.js.timers.setTimeout

case class JobProcessView(websocketPath: String
, runJobDialog: RunJobDialog)
case class JobProcessView(runJobDialog: RunJobDialog)
extends AdaptersClient {

@dom
protected def render: Binding[HTMLElement] = {
ClientWebsocket.connectWS(Some(websocketPath))
ClientWebsocket.connectWS()
<div>
{JobProcessHeader.showHeader().bind}{//
adapterContainer.bind}{//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import scala.language.implicitConversions
import scala.scalajs.js
import scala.scalajs.js.URIUtils

case class JobResultsView(websocketPath: String
, resultsInfos: CustomResultsInfos)
case class JobResultsView(resultsInfos: CustomResultsInfos)
(implicit concreteResult: ConcreteResult[JobResultsRow])
extends AdaptersClient
with ClientUtils {
Expand All @@ -32,7 +31,7 @@ case class JobResultsView(websocketPath: String

@dom
def render: Binding[HTMLElement] = {
ClientWebsocket.connectWS(Some(websocketPath))
ClientWebsocket.connectWS()
<div>
{adapterHeader.bind}{//
resultsTable.bind}
Expand Down Expand Up @@ -146,7 +145,7 @@ case class JobResultsView(websocketPath: String
val resultCount = s"$resultCountL=${clientConfig.resultCount}"
val filter = clientConfig.resultFilter.map(f => s"$resultFilterL=${URIUtils.encodeURIComponent(f)}").getOrElse("")
s"${clientConfig.jobConfig.webPath}?$resultCount&$filter"
}.foreach(path => ClientWebsocket.connectWS(Some(path)))
}.foreach(path => ClientWebsocket.connectWS())
}

@dom
Expand Down
14 changes: 11 additions & 3 deletions client/src/main/scala/pme123/adapters/client/UIStore.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,16 @@ object UIStore extends Logger {
.map(_.copy(resultFilter = newFilter))
}

def changeWebContext(context: String) {
info(s"UIStore: changeWebContext $context")
uiState.webContext.value = context
def changeWebContext(webContext: String): String = {
info(s"UIStore: changeWebContext $webContext")
uiState.webContext.value = webContext
webContext
}

def changeWebPath(webPath: String): String = {
info(s"UIStore: changeWebPath $webPath")
uiState.webPath.value = webPath
webPath
}

// make sure all are closed
Expand Down Expand Up @@ -183,6 +190,7 @@ case class UIState(logData: Vars[LogEntry] = Vars.empty[LogEntry]
, allClients: Vars[ClientConfig] = Vars.empty[ClientConfig]
, jobResultsRows: Vars[JobResultsRow] = Vars.empty[JobResultsRow]
, webContext: Var[String] = Var("")
, webPath: Var[String] = Var("")
)

object ToConcreteResults
Expand Down
32 changes: 17 additions & 15 deletions client/src/main/scala/pme123/adapters/client/demo/DemoClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import slogging.{ConsoleLoggerFactory, LoggerConfig}
import scala.language.implicitConversions
import scala.scalajs.js.annotation.JSExportTopLevel

case class DemoClient(websocketPath: String)
case class DemoClient()
extends AdaptersClient {

@dom
protected def render: Binding[HTMLElement] = {
ClientWebsocket.connectWS(Some(websocketPath))
ClientWebsocket.connectWS()
<div>
{imageContainer.bind}
</div>
Expand All @@ -43,28 +43,30 @@ object DemoClient
with Logger {

LoggerConfig.factory = ConsoleLoggerFactory()

// @JSExportTopLevel exposes this function with the defined name in Javascript.
// this is called by the index.scala.html of the server.
// the only connection that is not type-safe!
@JSExportTopLevel("client.DemoClient.main")
def main(context: String, websocketPath: String, clientType: String): Unit = {
info(s"DemoClient $clientType: $context$websocketPath")
def main(context: String, webPath: String, clientType: String): Unit = {
info(s"DemoClient $clientType: $context$webPath")
UIStore.changeWebContext(context)
UIStore.changeWebPath(webPath)

ClientType.withNameInsensitiveOption(clientType) match {
case Some(CUSTOM_PAGE) =>
DemoClient(websocketPath).create()
DemoClient().create()
case Some(JOB_PROCESS) =>
val jobDialog:RunJobDialog =
if(websocketPath.endsWith(DemoJobs.demoJobIdent))
DemoRunJobDialog
else
DefaultRunJobDialog
JobProcessView(websocketPath, jobDialog).create()
val jobDialog: RunJobDialog =
if (webPath.endsWith(DemoJobs.demoJobIdent))
DemoRunJobDialog
else
DefaultRunJobDialog
JobProcessView(jobDialog).create()
case Some(JOB_RESULTS) =>
JobResultsView(websocketPath
, CustomResultsInfos(Seq("Name", "Image Url", "Created")
,
s"""<ul>
JobResultsView(CustomResultsInfos(Seq("Name", "Image Url", "Created")
,
s"""<ul>
<li>name, imgUrl: String, * matches any part. Examples are name=Example*, subject=*Excel*</li>
<li>$dateTimeAfterL: take created from the defined DateTime, for example: 2017-12-22T12:00</li>
<li>$dateTimeBeforeL: take created until the defined DateTime, for example: 2018-01-22T23:00</li>
Expand Down
7 changes: 4 additions & 3 deletions project/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.typesafe.sbt.web.Import.Assets
import com.typesafe.sbt.web.SbtWeb.autoImport.pipelineStages
import org.scalajs.sbtplugin.ScalaJSPlugin.AutoImport.jsDependencies
import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
import org.scalajs.sbtplugin.Stage
import play.sbt.PlayImport.{filters, guice, ws}
import sbt.Keys._
Expand All @@ -17,10 +18,10 @@ object Settings {
lazy val orgId = "pme123"
lazy val orgHomepage = Some(new URL("https://github.com/pme123"))
lazy val projectName = "scala-adapters"
lazy val projectV = "1.4.0"
lazy val projectV = "1.4.1"

// main versions
lazy val scalaV = "2.12.4"
lazy val scalaV = "2.12.6"
lazy val bindingV = "11.0.1"
lazy val jQueryV = "2.2.4"
lazy val sloggingV = "0.6.1"
Expand Down Expand Up @@ -85,7 +86,7 @@ object Settings {
)
)
lazy val clientDependencies: Seq[Def.Setting[_]] = Def.settings(libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % "0.9.3"
"org.scala-js" %%% "scalajs-dom" % "0.9.6"
, "org.scala-lang.modules" %% "scala-xml" % "1.0.6"
, "com.typesafe.play" %%% "play-json" % "2.6.1"
, "com.thoughtworks.binding" %%% "dom" % bindingV
Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.1.0
sbt.version=1.1.6
7 changes: 3 additions & 4 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ resolvers += Resolver.url("heroku-sbt-plugin-releases",
resolvers += "jitpack" at "https://jitpack.io"

// Sbt plugins
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.13")
addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.6.15")

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6")

Expand All @@ -24,9 +24,8 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1")
addSbtPlugin("org.scoverage" % "sbt-coveralls" % "1.2.4")

// see https://github.com/portable-scala/sbt-crossproject
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.21")
addSbtPlugin("org.portable-scala" % "sbt-crossproject" % "0.3.1") // (1)
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.3.1") // (2)
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.23")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "0.4.0")

// version infos
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.7.0")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class AdaptersSettings(config: Config) extends Logger {
val adminMailActive: Boolean = baseConfig.getBoolean(adminMailActiveProp)
val adminMailRecipient: String = baseConfig.getString(adminMailRecipientProp)
val adminMailSubject: String = baseConfig.getString(adminMailSubjectProp)
val adminMailLoglevel: LogLevel = LogLevel.fromLevel(baseConfig.getString(adminMailLoglevelProp)).get
val adminMailLoglevel: LogLevel = LogLevel.withNameInsensitive(baseConfig.getString(adminMailLoglevelProp))
val wsocketHostsAllowed: Seq[String] = baseConfig.getStringList(wsocketHostsAllowedProp).asScala

val processLogEnabled: Boolean = baseConfig.getBoolean(processLogEnabledProp)
Expand Down