Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BE2] Added RumorStateAns to possible values of response #1889

Merged
merged 4 commits into from
May 29, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ object HighLevelProtocol extends DefaultJsonProtocol {
PARAM_MESSAGE_ID -> obj.message_id.toJson,
PARAM_WITNESS_SIG -> obj.witness_signatures.toJson
)

def fields: Set[String] = Set(PARAM_SENDER, PARAM_DATA, PARAM_SIGNATURE, PARAM_WITNESS_SIG, PARAM_MESSAGE_ID)
}

implicit object ParamsWithChannelFormat extends RootJsonFormat[ParamsWithChannel] {
Expand Down Expand Up @@ -225,7 +227,7 @@ object HighLevelProtocol extends DefaultJsonProtocol {
)
JsObject(jsObjContent)
}

def fields: Set[String] = Set(PARAM_SENDER_PK, PARAM_RUMOR_ID, PARAM_MESSAGES)
}

implicit object RumorStateFormat extends RootJsonFormat[RumorState] {
Expand All @@ -250,18 +252,30 @@ object HighLevelProtocol extends DefaultJsonProtocol {
implicit object ResultObjectFormat extends RootJsonFormat[ResultObject] {
override def read(json: JsValue): ResultObject = json match {
case JsNumber(resultInt) => new ResultObject(resultInt.toInt)
case JsArray(resultArray) => new ResultObject(resultArray.map(_.convertTo[Message]).toList)
case JsObject(resultMap) => new ResultObject(resultMap.map { case (k, v) => (Channel(k), v.convertTo[Set[Message]]) })
case _ => throw new IllegalArgumentException(s"Unrecognizable channel value in $json")
case JsArray(resultArray) =>
// in case of empty array, we cannot differentiate List[Rumor] and List[Message]
// We don't differentiate and use an EmptyList to make result available to different response handler
if (resultArray.isEmpty)
new ResultObject(ResultEmptyList())
resultArray.head.asJsObject.fields.keySet match
case keys if keys == RumorFormat.fields =>
new ResultObject(ResultRumor(resultArray.map(_.convertTo[Rumor]).toList))
case keys if keys == messageFormat.fields =>
new ResultObject(ResultMessage(resultArray.map(_.convertTo[Message]).toList))
case _ => throw new IllegalArgumentException(s"Can't parse jsArray $json to a ResultObject object")
case JsObject(resultMap) => new ResultObject(resultMap.map { case (k, v) => (Channel(k), v.convertTo[Set[Message]]) })
case _ => throw new IllegalArgumentException(s"Unrecognizable channel value in $json")
}

override def write(obj: ResultObject): JsValue = {
if (obj.isIntResult) {
JsNumber(obj.resultInt.getOrElse(0))
} else if (obj.resultMap.isDefined) {
JsObject(obj.resultMap.get.map { case (chan, set) => (chan.channel, set.toJson) })
} else if (obj.resultMessages.isDefined) {
JsArray(obj.resultMessages.get.map(m => m.toJson).toVector)
} else {
JsArray(obj.resultMessages.getOrElse(Nil).map(m => m.toJson).toVector)
JsArray(obj.resultRumor.getOrElse(Nil).map(r => r.toJson).toVector)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,61 @@
package ch.epfl.pop.model.network

import ch.epfl.pop.model.network.method.Rumor
import ch.epfl.pop.model.network.method.message.Message
import ch.epfl.pop.model.objects.Channel

class ResultObject(val resultInt: Option[Int], val resultMessages: Option[List[Message]], val resultMap: Option[Map[Channel, Set[Message]]]) {
sealed trait ResultType
final case class ResultInt(result: Int) extends ResultType
final case class ResultMessage(result: List[Message]) extends ResultType
final case class ResultMap(result: Map[Channel, Set[Message]]) extends ResultType
final case class ResultRumor(result: List[Rumor]) extends ResultType
final case class ResultEmptyList() extends ResultType

def this(result: Int) = this(Some(result), None, None)
class ResultObject(val result: Option[ResultType]) {

def this(result: List[Message]) = this(None, Some(result), None)
// sugar syntax and legacy purposes
def this(result: Int) = this(Some(ResultInt(result)))
def this(result: Map[Channel, Set[Message]]) = this(Some(ResultMap(result)))

def this(mapResult: Map[Channel, Set[Message]]) = this(None, None, Some(mapResult))
def this(result: ResultType) = this(Some(result))

def isIntResult: Boolean = resultInt.isDefined
def resultInt: Option[Int] = {
result match
case Some(resultInt: ResultInt) => Some(resultInt.result)
case _ => None
}

def resultMessages: Option[List[Message]] = {
result match
case Some(resultMessage: ResultMessage) => Some(resultMessage.result)
case Some(resultEmptyList: ResultEmptyList) => Some(List.empty)
case _ => None
}

def resultMap: Option[Map[Channel, Set[Message]]] = {
result match
case Some(resultMap: ResultMap) => Some(resultMap.result)
case _ => None
}

def resultRumor: Option[List[Rumor]] = {
result match
case Some(resultRumor: ResultRumor) => Some(resultRumor.result)
case Some(resultEmptyList: ResultEmptyList) => Some(List.empty)
case _ => None
}

def isIntResult: Boolean =
result match
case Some(_: ResultInt) => true
case _ => false

override def equals(o: Any): Boolean = {
o match {
override def equals(obj: Any): Boolean = {
obj match
case that: ResultObject =>
this.resultInt == that.resultInt && that.resultMessages == this.resultMessages && that.resultMap == this.resultMap
case _ => false
}
(this.result, that.result) match
case (Some(a), Some(b)) => a == b
case (None, None) => true
case _ => false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AnswerGenerator(dbActor: => AskableActorRef) extends AskPatternConstants {
val askCatchup = dbActor ? DbActor.Catchup(channel)
Await.ready(askCatchup, duration).value match {
case Some(Success(DbActor.DbActorCatchupAck(messages))) =>
val resultObject: ResultObject = new ResultObject(messages)
val resultObject: ResultObject = new ResultObject(ResultMessage(messages))
Right(JsonRpcResponse(RpcValidator.JSON_RPC_VERSION, Some(resultObject), None, rpcRequest.id))
case Some(Failure(ex: DbActorNAckException)) => Left(PipelineError(ex.code, s"AnswerGenerator failed : ${ex.message}", rpcRequest.getId))
case reply => Left(PipelineError(ErrorCodes.SERVER_ERROR.id, s"AnswerGenerator failed : unexpected DbActor reply '$reply'", rpcRequest.getId))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ package ch.epfl.pop.json
import ch.epfl.pop.IOHelper
import ch.epfl.pop.model.network.method.message.Message
import ch.epfl.pop.model.network.method.{GreetServer, ParamsWithChannel, ParamsWithMap, ParamsWithMessage, Rumor, RumorState}
import ch.epfl.pop.model.network.{JsonRpcRequest, JsonRpcResponse, MethodType, ResultObject}
import ch.epfl.pop.model.network.{JsonRpcRequest, JsonRpcResponse, MethodType, ResultMessage, ResultObject, ResultRumor}
import ch.epfl.pop.model.objects.*
import ch.epfl.pop.pubsub.graph.validators.RpcValidator
import org.scalatest.Inspectors.forEvery
import org.scalatest.funsuite.AnyFunSuite as FunSuite
import org.scalatest.matchers.should.Matchers
import spray.json.*
import util.examples.MessageExample
import util.examples.Rumor.RumorExample

import scala.collection.immutable.{HashMap, Set}

Expand Down Expand Up @@ -323,6 +325,24 @@ class HighLevelProtocolSuite extends FunSuite with Matchers {
answerFromJson.error should equal(None)
}

test("result object parses list of rumor correctly") {
val resultRumor: ResultObject = new ResultObject(ResultRumor(List(RumorExample.rumorExample)))
val resultRumorJsValue = HighLevelProtocol.ResultObjectFormat.write(resultRumor)
val resultRumorFromJson = HighLevelProtocol.ResultObjectFormat.read(resultRumorJsValue)

resultRumor shouldBe resultRumorFromJson

}

test("result object parses list of message correctly") {
val resultMessage: ResultObject = new ResultObject(ResultMessage(List(MessageExample.MESSAGE)))
val resultMessageJsValue = HighLevelProtocol.ResultObjectFormat.write(resultMessage)
val resultMessageFromJson = HighLevelProtocol.ResultObjectFormat.read(resultMessageJsValue)

resultMessage shouldBe resultMessageFromJson

}

test("Parser correctly encodes and decodes MethodType and rejects incorrect type") {
MethodType.values.foreach(obj => {
if obj != MethodType.INVALID then {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ package ch.epfl.pop.model.network

import ch.epfl.pop.model.network.method.message.Message
import ch.epfl.pop.model.objects.Channel
import org.scalatest.funsuite.{AnyFunSuite => FunSuite}
import org.scalatest.funsuite.AnyFunSuite as FunSuite
import org.scalatest.matchers.should.Matchers
import util.examples.Rumor.RumorExample

class ResultObjectSuite extends FunSuite with Matchers {
test("Int constructor works") {
Expand All @@ -15,10 +16,11 @@ class ResultObjectSuite extends FunSuite with Matchers {
}

test("List constructor works") {
val obj: ResultObject = new ResultObject(List.empty)
val obj: ResultObject = new ResultObject(ResultMessage(List.empty))

obj.resultInt should equal(None)
obj.resultMap should equal(None)
obj.resultRumor shouldBe None
obj.resultMessages should equal(Some(List.empty))
}

Expand All @@ -31,9 +33,19 @@ class ResultObjectSuite extends FunSuite with Matchers {

}

test("rumor list constructor works") {
val obj: ResultObject = new ResultObject(ResultRumor(List(RumorExample.rumorExample)))

obj.resultRumor shouldBe Some(List(RumorExample.rumorExample))
obj.resultInt shouldBe None
obj.resultMessages shouldBe None
obj.resultMap shouldBe None

}

test("isIntResult returns right result") {
val obj: ResultObject = new ResultObject(1)
val obj2: ResultObject = new ResultObject(List.empty)
val obj2: ResultObject = new ResultObject(ResultMessage(List.empty))
val obj3: ResultObject = new ResultObject(Map[Channel, Set[Message]]())

obj.isIntResult should equal(true)
Expand All @@ -44,17 +56,21 @@ class ResultObjectSuite extends FunSuite with Matchers {

test("equals works") {
val obj: ResultObject = new ResultObject(1)
val obj2: ResultObject = new ResultObject(List.empty)
val obj2: ResultObject = new ResultObject(ResultMessage(List.empty))
val obj5: ResultObject = new ResultObject(Map[Channel, Set[Message]]())
val obj3: ResultObject = new ResultObject(1)
val obj4: ResultObject = new ResultObject(List.empty)
val obj4: ResultObject = new ResultObject(ResultMessage(List.empty))
val obj6: ResultObject = new ResultObject(Map[Channel, Set[Message]]())
val rumorResult1: ResultObject = new ResultObject(ResultRumor(List(RumorExample.rumorExample)))
val rumorResult2: ResultObject = new ResultObject(ResultRumor(List(RumorExample.rumorExample)))

obj.equals(obj3) should equal(true)
obj2.equals(obj4) should equal(true)
obj2.equals(obj) should equal(false)
obj5.equals(obj6) should equal(true)
obj5.equals(obj) should equal(false)
obj6.equals(obj2) should equal(false)
rumorResult1.equals(rumorResult2) shouldBe true
rumorResult1.equals(obj4) shouldBe false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import akka.pattern.{AskableActorRef, ask}
import akka.testkit.{ImplicitSender, TestKit}
import akka.util.Timeout
import ch.epfl.pop.model.network.method.message.Message
import ch.epfl.pop.model.network.{JsonRpcRequest, JsonRpcResponse, ResultObject}
import ch.epfl.pop.model.network.{JsonRpcRequest, JsonRpcResponse, ResultMessage, ResultObject}
import ch.epfl.pop.model.objects.DbActorNAckException
import ch.epfl.pop.pubsub.graph.validators.RpcValidator
import ch.epfl.pop.pubsub.graph.validators.SchemaVerifierSuite._
import ch.epfl.pop.pubsub.graph.validators.SchemaVerifierSuite.*
import ch.epfl.pop.storage.DbActor
import org.scalatest.BeforeAndAfterAll
import org.scalatest.funsuite.{AnyFunSuiteLike => FunSuiteLike}
import org.scalatest.funsuite.AnyFunSuiteLike as FunSuiteLike
import org.scalatest.matchers.should.Matchers
import util.examples.MessageExample

Expand Down Expand Up @@ -94,7 +94,7 @@ class AnswerGeneratorSuite extends TestKit(ActorSystem("Test")) with FunSuiteLik
lazy val dbActorRef = mockDbWithMessages(Nil)
val message: GraphMessage = new AnswerGenerator(dbActorRef).generateAnswer(Right(rpcCatchupReq))

def resultObject: ResultObject = new ResultObject(Nil)
def resultObject: ResultObject = new ResultObject(ResultMessage(Nil))

val expected = Right(JsonRpcResponse(
RpcValidator.JSON_RPC_VERSION,
Expand All @@ -113,7 +113,7 @@ class AnswerGeneratorSuite extends TestKit(ActorSystem("Test")) with FunSuiteLik
lazy val dbActorRef = mockDbWithMessages(messages)
val gmsg: GraphMessage = new AnswerGenerator(dbActorRef).generateAnswer(Right(rpcCatchupReq))

def resultObject: ResultObject = new ResultObject(messages)
def resultObject: ResultObject = new ResultObject(ResultMessage(messages))

val expected = Right(JsonRpcResponse(
RpcValidator.JSON_RPC_VERSION,
Expand Down
Loading