Finalized JSON deserialization

This commit is contained in:
Rohan Sircar 2020-05-19 10:29:11 +05:30
parent 7b01a60d82
commit 1221a95c72
9 changed files with 251 additions and 41 deletions

View File

@ -42,7 +42,7 @@ libraryDependencies ++= Seq(
"org.scalafx" %% "scalafx" % "12.0.2-R18", "org.scalafx" %% "scalafx" % "12.0.2-R18",
"org.scalafx" %% "scalafx-extras" % "0.3.4", "org.scalafx" %% "scalafx-extras" % "0.3.4",
"com.softwaremill.sttp.client" %% "json4s" % "2.1.1", "com.softwaremill.sttp.client" %% "json4s" % "2.1.1",
"org.json4s" %% "json4s-native" % "3.6.7", "org.json4s" %% "json4s-jackson" % "3.6.8",
"org.scala-lang.modules" %% "scala-async" % "0.10.0", "org.scala-lang.modules" %% "scala-async" % "0.10.0",
"org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided, "org.scala-lang" % "scala-reflect" % scalaVersion.value % Provided,
// "org.kordamp.ikonli" %% "ikonli-javafx" % "11.4.0", // "org.kordamp.ikonli" %% "ikonli-javafx" % "11.4.0",
@ -63,6 +63,9 @@ libraryDependencies += "com.jfoenix" % "jfoenix" % "9.0.9"
// https://mvnrepository.com/artifact/org.kordamp.bootstrapfx/bootstrapfx-core // https://mvnrepository.com/artifact/org.kordamp.bootstrapfx/bootstrapfx-core
libraryDependencies += "org.kordamp.bootstrapfx" % "bootstrapfx-core" % "0.2.4" libraryDependencies += "org.kordamp.bootstrapfx" % "bootstrapfx-core" % "0.2.4"
// https://mvnrepository.com/artifact/org.json4s/json4s-ext
libraryDependencies += "org.json4s" %% "json4s-ext" % "3.6.8"
enablePlugins(BuildInfoPlugin) enablePlugins(BuildInfoPlugin)
buildInfoPackage := "wow.doge.chatto" buildInfoPackage := "wow.doge.chatto"

View File

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?> <?xml version="1.0" encoding="UTF-8" ?>
<?import com.jfoenix.controls.JFXListView ?> <?import com.jfoenix.controls.JFXListView ?>
<?import java.net.URL ?>
<?import javafx.geometry.Insets ?> <?import javafx.geometry.Insets ?>
<?import javafx.scene.control.Button ?> <?import javafx.scene.control.Button ?>
<?import javafx.scene.control.Label ?> <?import javafx.scene.control.Label ?>
@ -25,15 +24,24 @@
<center> <center>
<VBox prefHeight="200.0" prefWidth="100.0" spacing="5.0" BorderPane.alignment="CENTER"> <VBox prefHeight="200.0" prefWidth="100.0" spacing="5.0" BorderPane.alignment="CENTER">
<children> <children>
<BorderPane prefHeight="41.0" prefWidth="620.0">
<center>
<HBox alignment="CENTER">
<children>
<Label id="curUser" fx:id="curUsr" text="User1" />
<Label id="content" fx:id="dataContent" text="User2" />
</children>
</HBox>
</center>
</BorderPane>
<JFXListView fx:id="chatListView" prefHeight="463.0" prefWidth="610.0" /> <JFXListView fx:id="chatListView" prefHeight="463.0" prefWidth="610.0" />
<HBox prefHeight="50.0" prefWidth="790.0" spacing="2.0"> <HBox prefHeight="50.0" prefWidth="790.0" spacing="2.0">
<children> <children>
<Label fx:id="label" contentDisplay="CENTER" maxWidth="100.0" prefHeight="30.0" prefWidth="52.0" text="Label" />
<TextArea fx:id="chatInput" prefHeight="15.0" prefWidth="250.0" HBox.hgrow="ALWAYS" /> <TextArea fx:id="chatInput" prefHeight="15.0" prefWidth="250.0" HBox.hgrow="ALWAYS" />
<FlowPane alignment="CENTER" hgap="2.0" prefHeight="47.0" prefWidth="221.0"> <FlowPane alignment="CENTER" hgap="2.0" prefHeight="47.0" prefWidth="221.0">
<children> <children>
<Button fx:id="logoutButton" styleClass="btn, btn-primary" text="Logout" /> <Button fx:id="logoutButton" styleClass="btn, btn-primary" text="Logout" onAction="#actionLogout" />
<Button fx:id="submitButton" styleClass="btn, btn-primary" text="Submit" /> <Button fx:id="submitButton" styleClass="btn, btn-primary" text="Submit" />
</children> </children>

View File

@ -8,6 +8,10 @@
</encoder> </encoder>
</appender> </appender>
<logger name="wow.doge.chatto" level="DEBUG" />
<logger name="com.sfxcode.sapphire" level="DEBUG" />
<root level="info"> <root level="info">
<appender-ref ref="STDOUT" /> <appender-ref ref="STDOUT" />
</root> </root>

View File

@ -13,7 +13,9 @@ import scala.concurrent.ExecutionContext.Implicits.global
import scala.async.Async.{async, await} import scala.async.Async.{async, await}
import sttp.client.json4s._ import sttp.client.json4s._
import org.json4s._ import org.json4s._
import org.json4s.native.JsonMethods._ // import org.json4s.native.JsonMethods._
import org.json4s.jackson.JsonMethods._
import org.json4s.JsonDSL._ import org.json4s.JsonDSL._
import scala.util.Success import scala.util.Success
import scala.util.Failure import scala.util.Failure
@ -28,7 +30,7 @@ class ApplicationController extends DefaultWindowController {
// import ApplicationController._ // import ApplicationController._
lazy val mainViewController = getController[MainViewController]() lazy val mainViewController = getController[MainViewController]()
private implicit lazy val serialization = org.json4s.native.Serialization private implicit lazy val serialization = org.json4s.jackson.Serialization
private implicit lazy val backend = AsyncHttpClientFutureBackend() private implicit lazy val backend = AsyncHttpClientFutureBackend()
// override def width: Int = 400 // override def width: Int = 400

View File

@ -0,0 +1,12 @@
package wow.doge.chatto.control
import javafx.scene.layout.HBox
import javafx.scene.control.Label
import scalafx.Includes._
class UserBox2(_username: String) extends HBox() {
val usernameLabel = new Label(_username)
this.children ++= Seq {
usernameLabel
}
}

View File

@ -24,57 +24,102 @@ import wow.doge.chatto.AppDataHandler
import com.jfoenix.controls.JFXListView import com.jfoenix.controls.JFXListView
import scala.async.Async.{async, await} import scala.async.Async.{async, await}
import javafx.scene.paint.Color import javafx.scene.paint.Color
import scalafx.collections.ObservableBuffer
import javafx.beans.property.SimpleListProperty
import wow.doge.chatto.control.UserBox2
import javafx.beans.value.ChangeListener
import com.sfxcode.sapphire.core.value.KeyBindings
import com.sfxcode.sapphire.core.value.FXBeanAdapter
import scalafx.collections.ObservableMap
import com.sfxcode.sapphire.core.value.BeanConversions
import javafx.util.converter.DateStringConverter
class ChatController @Inject() ( class ChatController @Inject() (
userService: UserService, userService: UserService,
appDataHandler: AppDataHandler appDataHandler: AppDataHandler
) extends AbstractViewController ) extends AbstractViewController
with LazyLogging { with LazyLogging
with BeanConversions {
@FXML private var label: Label = _ // @FXML private var label: Label = _
@FXML private var flowPane: FlowPane = _ @FXML private var flowPane: FlowPane = _
@FXML private var submitButton: Button = _ @FXML private var submitButton: Button = _
@FXML private var logoutButton: Button = _ @FXML private var logoutButton: Button = _
// @FXML private var chatTextArea: TextArea = _ // @FXML private var chatTextArea: TextArea = _
@FXML private var chatInput: TextArea = _ @FXML private var chatInput: TextArea = _
@FXML private var usersVBox: VBox = _ @FXML private var usersVBox: VBox = _
@FXML var usersListView: JFXListView[Any] = _ @FXML var usersListView: JFXListView[UserBox2] = _
@FXML private var chatListView: JFXListView[HBox] = _ @FXML private var chatListView: JFXListView[HBox] = _
@FXML private var curUsr: Label = _
@FXML private var dataContent: Label = _
// applicationController.show // applicationController.show
private val usersBuffer = ObservableBuffer[FXBean[User]]()
private val chatDataBuffer = ObservableMap[String, FXBean[User]]()
private lazy val curUserKeys = KeyBindings("curUser")
// bindings.add("person", "Person ${_self.name()} with age of ${_self.age()} is active: ${_self.isActive()}")
private lazy val curUserAdapter = FXBeanAdapter[User](this)
override def didGainVisibilityFirstTime(): Unit = { override def didGainVisibilityFirstTime(): Unit = {
super.didGainVisibilityFirstTime() super.didGainVisibilityFirstTime()
val ub = new UserBox() // val ub = new UserBox()
this.stage.resizable = true this.stage.resizable = true
ub.messageLabel.text = "Hi there" // ub.messageLabel.text = "Hi there"
// ub.messageLabel.id = // ub.messageLabel.id =
ub.userRadioButton.text = "User 1" // ub.userRadioButton.text = "User 1"
// usersVBox.children.clear() // usersVBox.children.clear()
// usersVBox.children += ub // usersVBox.children += ub
println("test") // println("test")
println(s"Result = ${func()}") // println(s"Result = ${func()}")
offFX(println("hello from new thread")) // offFX(println("hello from new thread"))
// chatTextArea.visible <== !chatInput.text.isEmpty // chatTextArea.visible <== !chatInput.text.isEmpty
// chatTextArea.text <== chatInput.text // chatTextArea.text <== chatInput.text
for (r <- userService.func2()) yield (logger.info(s"${r.body}")) // for (r <- userService.func2()) yield (logger.info(s"${r.body}"))
val person = Person(0, 10, "Billy") // val person = Person(0, 10, "Billy")
val bean = FXBean[Person](person) // val bean = FXBean[Person](person)
ub.messageLabel.text <== bean.getStringProperty("name") // ub.messageLabel.text <== bean.getStringProperty("name")
// bean.getStringProperty("name") <== chatInput.text // bean.getStringProperty("name") <== chatInput.text
// bean.getStringProperty("name")() = "Lester" // bean.getStringProperty("name")() = "Lester"
println(bean.getValue("name")) // println(bean.getValue("name"))
println(bean.getStringProperty("name")()) // println(bean.getStringProperty("name")())
// bean. // bean.
bean.updateValue("name", "Lester") // bean.updateValue("name", "Lester")
println(bean.bean) // println(bean.bean)
logoutButton.onAction = (e) => { // val curUserBean = FXBean[User](User("None"))
applicationController.logout()
println(appDataHandler.appData) // logoutButton.onAction = (e) =>
// usersListView
// .getSelectionModel()
// .selectedItemProperty()
// .addListener((_, _, newValue) => {
// curUsr.text() = newValue.usernameLabel.text()
// })
curUserKeys.add("content", "${_self.data().content()}")
// curUserAdapter.addDateConverter()
curUserAdapter.addBindings(curUserKeys)
usersListView
.getSelectionModel()
.selectedItemProperty()
.addListener((_, _, newValue) => {
if (newValue != null) {
val dataBean =
FXBean(User(newValue.usernameLabel.text(), Data("test data")))
curUserAdapter
.set(dataBean)
} }
})
// curUsr.text <== usersListView
// .getSelectionModel()
// .getSelectedItem()
// .username
// .text
} }
@ -86,9 +131,19 @@ class ChatController @Inject() (
val willBeUsers = userService val willBeUsers = userService
.getUsers(appDataHandler.appData.credentials) .getUsers(appDataHandler.appData.credentials)
.map(_.body) .map(_.body)
val willBeActiveUsers = userService
.getActiveUsers(appDataHandler.appData.credentials)
.map(_.body)
val maybeUsers = await(willBeUsers) val maybeUsers = await(willBeUsers)
val maybeActiveUsers = await(willBeActiveUsers)
// maybeActiveUsers.foreach(println)
logger.debug(s"$maybeActiveUsers")
val maybeUsersBoxes = maybeUsers.map(users => { val maybeUsersBoxes = maybeUsers.map(users => {
// usersBuffer ++= users
users.map(user => { users.map(user => {
// usersBuffer += FXBean(User(user))
// val ub = new UserBox() // val ub = new UserBox()
// ub.messageLabel.text = "Hi there" // ub.messageLabel.text = "Hi there"
// ub.userRadioButton.text = user // ub.userRadioButton.text = user
@ -96,12 +151,12 @@ class ChatController @Inject() (
// new Rectangle { // new Rectangle {
// fill <== when(hover) choose (Color.Red) // fill <== when(hover) choose (Color.Red)
// } // }
val hb = new HBox // val hb = new HBox
new HBox { new UserBox2(user) {
this.children += new Label { // this.children += new Label {
textProperty() = user // textProperty() = user
// textFillProperty <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE) // // textFillProperty <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
} // }
// fill <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE) // fill <== when(this.hover) choose (Color.RED) otherwise (Color.BLUE)
} }
}) })
@ -109,6 +164,8 @@ class ChatController @Inject() (
onFX { onFX {
maybeUsersBoxes.map(userBoxes => { maybeUsersBoxes.map(userBoxes => {
usersListView.items() ++= userBoxes usersListView.items() ++= userBoxes
// val x = new SimpleListProperty(ObservableBuffer(userBoxes))
// usersListView.items <== x
}) })
} }
} }
@ -119,6 +176,24 @@ class ChatController @Inject() (
} }
x x
} }
def actionLogout = {
offFXAndWait {
appDataHandler.clearCredentials()
println(appDataHandler.appData)
}
curUserAdapter.set(User.empty)
usersListView.items().clear()
chatListView.items().clear()
applicationController.logout()
println(appDataHandler.appData)
}
} }
final case class Person(id: Int, age: Int, name: String) final case class Person(id: Int, age: Int, name: String)
final case class Data(content: String)
final case class User(curUser: String, data: Data)
object User {
def empty = User("", Data(""))
}
// final case class chatData()

View File

@ -65,7 +65,7 @@ class LoginController @Inject() (
val box = new VBox() val box = new VBox()
val adapter = FXBeanAdapter[Person](this) val adapter = FXBeanAdapter[Person](this)
val bean = FXBean[Person](Person("twar", "username", "password")) val bean = FXBean[Person](Person("0", "username", "password"))
adapter.addBindings(bindings) adapter.addBindings(bindings)
adapter.set(bean) adapter.set(bean)
// adapter.addIntConverter("age") // adapter.addIntConverter("age")
@ -96,9 +96,9 @@ class LoginController @Inject() (
val credentials = val credentials =
UserCredentials(inputUserName, inputPassword, token) UserCredentials(inputUserName, inputPassword, token)
appDataHandler.updateCredentials(credentials) appDataHandler.updateCredentials(credentials)
await { // await {
userService.getUsers(credentials).map(_.body.foreach(println)) // userService.getUsers(credentials).map(_.body.foreach(println))
} // }
applicationController.showChatPane() applicationController.showChatPane()
} }
case None => { case None => {

View File

@ -0,0 +1,69 @@
package wow.doge.chatto.service
import org.json4s.CustomSerializer
import org.json4s._
import java.time.Instant
// import org.json4s.JsonAST._
import org.json4s.JsonDSL._
// class ParentSerializer extends Serializer[Parent] {
// private val ParentClass = classOf[Parent]
// implicit val formats = DefaultFormats
// def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
// case (TypeInfo(ParentClass, _), json) => json match {
// case JObject(JField("kind", JString(kind)) :: _) => kind match {
// case "first_type" => json.extract[ChildClassOne]
// case "second_type" => json.extract[ChildClassTwo]
// }
// case _ => throw new MappingException("Invalid kind")
// }
// }
// def serialize(implicit format: Formats): PartialFunction[Any, JValue] = Map()
// }
// class MyJInstantSerializer extends Serializer[Instant] {
// implicit val formats = DefaultFormats
// def deserialize(
// implicit format: Formats
// ): PartialFunction[(TypeInfo, JValue), Instant] = {
// case (_, JString(d)) => Instant.parse(d)
// case (_, JNull) => null
// }
// def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
// case d: Instant => JString(d.toString())
// }
// }
// class MyJInstantSerializer
// extends CustomSerializer[Instant](implicit format =>
// (
// {
// // case JInt(d) => Instant.ofEpochMilli(d.toLong)
// case JString(d) => Instant.parse(d)
// case JNull => null
// }, {
// // case d: Instant => JInt(d.toEpochMilli)
// case d: Instant => JString(d.toString())
// }
// )
// )
// import java.time.ZonedDateTime
// import java.time.format.DateTimeFormatter
// a custom serializer has two partial functions, one
// for serializing and one for deserializing
// case object ZDTSerializer
// extends CustomSerializer[ZonedDateTime](format =>
// ({
// case JString(s) => ZonedDateTime.parse(s)
// }, {
// case zdt: ZonedDateTime =>
// JString(
// zdt.format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"))
// )
// })
// )

View File

@ -9,7 +9,6 @@ import sttp.client._
import scala.concurrent.Future import scala.concurrent.Future
import sttp.client.asynchttpclient.WebSocketHandler import sttp.client.asynchttpclient.WebSocketHandler
import javax.inject.Inject import javax.inject.Inject
import scala.async.Async.{async, await}
import scala.util.Success import scala.util.Success
import scala.util.Failure import scala.util.Failure
import wow.doge.chatto.AppData import wow.doge.chatto.AppData
@ -20,17 +19,23 @@ import wow.doge.chatto.ApplicationController
import wow.doge.chatto.UserCredentials import wow.doge.chatto.UserCredentials
import javax.inject._ import javax.inject._
import wow.doge.chatto.AppDataHandler import wow.doge.chatto.AppDataHandler
import org.json4s.jackson.JsonMethods._
import org.json4s.ext.JavaTimeSerializers
import java.time.ZonedDateTime
import org.json4s.jackson.Serialization._
class UserService @Inject() (appDataHandler: AppDataHandler)( class UserService @Inject() (appDataHandler: AppDataHandler)(
implicit backend: HttpBackend implicit backend: HttpBackend
) extends LazyLogging { ) extends LazyLogging {
private implicit lazy val serialization = org.json4s.native.Serialization private implicit lazy val serialization = org.json4s.jackson.Serialization
private implicit lazy val formats =
DefaultFormats ++ JavaTimeSerializers.all
private val domain = "http://localhost:8080" private val domain = "http://localhost:8080"
private lazy val baseUrl = uri"$domain/api/chat" private lazy val baseUrl = uri"$domain/api/chat"
private lazy val authBasicRequest = (credentials: UserCredentials) => private lazy val authBasicRequest = (credentials: UserCredentials) =>
basicRequest.auth basicRequest.auth
.basic(credentials.username, credentials.password) .basic(credentials.username, credentials.password)
// .header("X-AUTH-TOKEN", appData.credentials.token) .header("X-AUTH-TOKEN", credentials.token)
def func1() = async { def func1() = async {
val willBeResponse = func2() val willBeResponse = func2()
@ -47,7 +52,12 @@ class UserService @Inject() (appDataHandler: AppDataHandler)(
private def endpoint(uri: String) = uri"$baseUrl/$uri" private def endpoint(uri: String) = uri"$baseUrl/$uri"
def getUsers(credentials: UserCredentials) = async { def getUsers(credentials: UserCredentials) = async {
println(appDataHandler.appData) logger.debug(s"${appDataHandler.appData}")
println(
write[ActiveUser](
ActiveUser("hmm what is it", true, Some(ZonedDateTime.now()))
)
)
await { await {
authBasicRequest(credentials) authBasicRequest(credentials)
.get(uri"http://localhost:8080/api/chat/get/users") .get(uri"http://localhost:8080/api/chat/get/users")
@ -56,6 +66,22 @@ class UserService @Inject() (appDataHandler: AppDataHandler)(
} }
} }
def getMessages(credentials: UserCredentials) = async {
logger.debug(s"${appDataHandler.appData}")
await {
authBasicRequest(credentials)
.get(uri"http://localhost:8080/api/chat/get/users")
.response(asJson[List[String]])
.send()
}
}
def getActiveUsers(credentials: UserCredentials) =
authBasicRequest(credentials)
.get(uri"http://localhost:8080/api/chat/get/active-users")
.response(asJson[List[ActiveUser]])
.send()
} }
final case class HttpBinResponse( final case class HttpBinResponse(
@ -63,3 +89,14 @@ final case class HttpBinResponse(
origin: String, origin: String,
headers: Map[String, String] headers: Map[String, String]
) )
final case class ActiveUser(
userName: String,
online: Boolean,
lastActive: Option[ZonedDateTime]
)
object ActiveUser {
val z = ZonedDateTime.now()
z.toInstant().toString()
}