From 3e2506f5e05d0491a4a05a93e919eda1821dee6f Mon Sep 17 00:00:00 2001 From: Na Lee Ha Date: Fri, 23 Aug 2024 12:29:31 +0100 Subject: [PATCH] #1434 using new rpc message for view port typeahead request --- .../vuu/net/rpc/RpcMethodHandlerTest.java | 1 - .../finos/vuu/core/CoreServerApiHandler.scala | 13 +- .../ViewPortTypeAheadRpcHandler.scala | 27 ++-- .../scala/org/finos/vuu/net/Messages.scala | 19 ++- .../org/finos/vuu/wsapi/TableWSApiTest.scala | 10 +- .../finos/vuu/wsapi/TypeAheadWSApiTest.scala | 140 +++++++----------- .../vuu/wsapi/WebSocketApiTestBase.scala | 10 +- 7 files changed, 95 insertions(+), 125 deletions(-) diff --git a/vuu-java/src/test/java/org/finos/vuu/net/rpc/RpcMethodHandlerTest.java b/vuu-java/src/test/java/org/finos/vuu/net/rpc/RpcMethodHandlerTest.java index c8c9e0ce7..9fc5f0af2 100644 --- a/vuu-java/src/test/java/org/finos/vuu/net/rpc/RpcMethodHandlerTest.java +++ b/vuu-java/src/test/java/org/finos/vuu/net/rpc/RpcMethodHandlerTest.java @@ -12,7 +12,6 @@ import java.util.Collections; -import static org.assertj.core.api.Assertions.assertThat; import static test.helper.ViewPortTestUtils.createRandomViewServerMessage; public class RpcMethodHandlerTest { diff --git a/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala b/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala index 200bfde6b..c6a8906d7 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/CoreServerApiHandler.scala @@ -417,14 +417,21 @@ class CoreServerApiHandler(val viewPortContainer: ViewPortContainer, logger.info(s"Processed VP RPC call ${ctx.requestId}" + msg) functionResult match { case RpcFunctionSuccess(data) => - RpcResponseNew(rpcName = msg.rpcName, result = RpcResult(true, data, null), null) + RpcResponseNew(rpcName = msg.rpcName, result = RpcResult(isSuccess = true, data, null), null) case RpcFunctionFailure(_, error, exception) => - RpcResponseNew(rpcName = msg.rpcName, RpcResult(false, null, errorMessage = error), null) + createErrorRpcResponse(msg, error) } case Failure(e) => logger.info(s"Failed to process VP RPC call ${ctx.requestId}", e) - RpcResponseNew(rpcName = msg.rpcName, RpcResult(false, null, errorMessage = e.getMessage), null) + createErrorRpcResponse(msg, e.getMessage) } vsMsg(response)(ctx) } + + private def createErrorRpcResponse(msg: RpcRequest, errorMessage: String) = { + RpcResponseNew( + rpcName = msg.rpcName, + result = RpcResult(isSuccess = false, null, errorMessage = errorMessage), + action = ShowNotificationAction("Error", s"Failed to process ${msg.rpcName} request", errorMessage)) + } } \ No newline at end of file diff --git a/vuu/src/main/scala/org/finos/vuu/core/module/typeahead/ViewPortTypeAheadRpcHandler.scala b/vuu/src/main/scala/org/finos/vuu/core/module/typeahead/ViewPortTypeAheadRpcHandler.scala index 4630c1c17..893112984 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/module/typeahead/ViewPortTypeAheadRpcHandler.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/module/typeahead/ViewPortTypeAheadRpcHandler.scala @@ -8,11 +8,10 @@ import org.finos.vuu.viewport.ViewPortColumns class ViewPortTypeAheadRpcHandler(tableContainer: TableContainer) extends DefaultRpcHandler with StrictLogging { - this.registerRpc(RpcNames.UniqueFieldValuesRpc, params => processGetUniqueFieldValuesRequestNew(params)) + this.registerRpc(RpcNames.UniqueFieldValuesRpc, params => processGetUniqueFieldValuesRequest(params)) this.registerRpc(RpcNames.UniqueFieldValuesStartWithRpc, params => processGetUniqueFieldValuesStartWithRequest(params)) - - def processGetUniqueFieldValuesRequestNew(params: RpcParams): RpcFunctionResult = { + def processGetUniqueFieldValuesRequest(params: RpcParams): RpcFunctionResult = { val inputParam = params.data.get.asInstanceOf[Map[String, Any]] @@ -26,23 +25,15 @@ class ViewPortTypeAheadRpcHandler(tableContainer: TableContainer) extends Defaul new RpcFunctionSuccess(values) } - def processGetUniqueFieldValuesRequest(params: RpcParams): RpcFunctionResult = { - val values = getUniqueFieldValues( - params.namedParams("table").toString, //how to report error when expected param missing or fail to cast to right type - params.namedParams("module").toString, - params.namedParams("column").toString, - params.viewPortColumns.get, - null //todo what to do about request context - ) - new RpcFunctionSuccess(values) - } - def processGetUniqueFieldValuesStartWithRequest(params: RpcParams): RpcFunctionResult = { + + val inputParam = params.data.get.asInstanceOf[Map[String, Any]] + val values = getUniqueFieldValuesStartingWith( - params.namedParams("table").toString, //how to report error when expected param missing or fail to cast to right type - params.namedParams("module").toString, - params.namedParams("column").toString, - params.namedParams("starts").toString, + inputParam("table").toString, //how to report error when expected param missing or fail to cast to right type + inputParam("module").toString, + inputParam("column").toString, + inputParam("starts").toString, params.viewPortColumns.get, null //todo what to do about request context ) diff --git a/vuu/src/main/scala/org/finos/vuu/net/Messages.scala b/vuu/src/main/scala/org/finos/vuu/net/Messages.scala index 584f08ac9..f9be2bc44 100644 --- a/vuu/src/main/scala/org/finos/vuu/net/Messages.scala +++ b/vuu/src/main/scala/org/finos/vuu/net/Messages.scala @@ -1,6 +1,7 @@ package org.finos.vuu.net -import com.fasterxml.jackson.annotation.JsonTypeInfo +import com.fasterxml.jackson.annotation.JsonSubTypes.Type +import com.fasterxml.jackson.annotation.{JsonSubTypes, JsonTypeInfo} import com.fasterxml.jackson.databind.annotation.{JsonDeserialize, JsonSerialize, JsonTypeIdResolver} import org.finos.vuu.api.AvailableViewPortVisualLink import org.finos.vuu.net.json.{RowUpdateDeserializer, RowUpdateSerializer} @@ -226,10 +227,12 @@ case class RpcResult(isSuccess: Boolean, data: Any, errorMessage: String) // override val isSuccess: Boolean = false //} -trait UIAction { - val actionType: String -} - -case class ShowNotificationAction(notificationType: String, title: String, message: String) extends UIAction { - override val actionType: String = "SHOW_NOTIFICATION_ACTION" -} \ No newline at end of file +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") +@JsonSubTypes(Array( + new Type(value = classOf[NoAction], name = "NO_ACTION"), + new Type(value = classOf[ShowNotificationAction], name = "SHOW_NOTIFICATION_ACTION"), +)) +trait UIAction + +case class NoAction() extends UIAction +case class ShowNotificationAction(notificationType: String, title: String, message: String) extends UIAction \ No newline at end of file diff --git a/vuu/src/test/scala/org/finos/vuu/wsapi/TableWSApiTest.scala b/vuu/src/test/scala/org/finos/vuu/wsapi/TableWSApiTest.scala index bf817bf8e..a879357cb 100644 --- a/vuu/src/test/scala/org/finos/vuu/wsapi/TableWSApiTest.scala +++ b/vuu/src/test/scala/org/finos/vuu/wsapi/TableWSApiTest.scala @@ -17,8 +17,8 @@ class TableWSApiTest extends WebSocketApiTestBase { private val moduleName = "TEST" - Feature("Server web socket api") { - Scenario("client requests to get table metadata for a table") { + Feature("[Web Socket API] Get table metadata") { + Scenario("For a table") { val requestId = vuuClient.send(sessionId, tokenId, GetTableMetaRequest(ViewPortTable("TableMetaTest", moduleName))) @@ -30,7 +30,7 @@ class TableWSApiTest extends WebSocketApiTestBase { responseBody.columns shouldEqual Array("Id", "Account") } - Scenario("client requests to get table metadata for a table with no view port def defined") { + Scenario("For a table with no view port def defined") { val requestId = vuuClient.send(sessionId, tokenId, GetTableMetaRequest(ViewPortTable("TableMetaDefaultVPTest", moduleName))) @@ -42,7 +42,7 @@ class TableWSApiTest extends WebSocketApiTestBase { responseBody.columns shouldEqual Array("Id") } - Scenario("client requests to get table metadata for a non existent") { + Scenario("For a non existent table") { val requestId = vuuClient.send(sessionId, tokenId, GetTableMetaRequest(ViewPortTable("DoesNotExist", moduleName))) @@ -53,7 +53,7 @@ class TableWSApiTest extends WebSocketApiTestBase { responseBody.msg shouldEqual "No such table found with name DoesNotExist in module " + moduleName } - Scenario("client requests to get table metadata for null table name") { + Scenario("For null table name") { val requestId = vuuClient.send(sessionId, tokenId, GetTableMetaRequest(ViewPortTable(null, moduleName))) diff --git a/vuu/src/test/scala/org/finos/vuu/wsapi/TypeAheadWSApiTest.scala b/vuu/src/test/scala/org/finos/vuu/wsapi/TypeAheadWSApiTest.scala index abc5171cc..4e1d6aa74 100644 --- a/vuu/src/test/scala/org/finos/vuu/wsapi/TypeAheadWSApiTest.scala +++ b/vuu/src/test/scala/org/finos/vuu/wsapi/TypeAheadWSApiTest.scala @@ -7,7 +7,7 @@ import org.finos.vuu.core.module.{ModuleFactory, ViewServerModule} import org.finos.vuu.core.table.{DataTable, TableContainer} import org.finos.vuu.net._ import org.finos.vuu.provider.{Provider, ProviderContainer} -import org.finos.vuu.viewport.{DisplayResultAction, ViewPortRange, ViewPortTable} +import org.finos.vuu.viewport.{ViewPortRange, ViewPortTable} import org.finos.vuu.wsapi.helpers.TestExtension.ModuleFactoryExtension import org.finos.vuu.wsapi.helpers.{FakeDataSource, TestProvider} @@ -19,14 +19,14 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { private val tableNameEmpty = "TypeaheadTestEmpty" private val moduleName = "TEST" - Feature("Server web socket api") { - Scenario("Type ahead request for a column using new request") { + Feature("[Web Socket API] Type ahead request") { + Scenario("For a column") { Given("a view port exist") val viewPortId: String = createViewPort When("request typeahead for Account column") - val getTypeAheadRequest = createTypeAheadRequestNew(viewPortId, tableName, "Account") + val getTypeAheadRequest = createTypeAheadRequest(viewPortId, tableName, "Account") val requestId = vuuClient.send(sessionId, tokenId, getTypeAheadRequest) Then("return top 10 unique values in that column") @@ -34,52 +34,30 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) responseBody.rpcName shouldEqual "getUniqueFieldValues" - // responseBody.result.isSuccess shouldBe true - //val result = responseBody.result.asInstanceOf[RpcSuccessResult] responseBody.result.isSuccess shouldEqual true responseBody.result.data shouldEqual List("12355", "45321", "89564", "42262", "65879", "88875", "45897", "23564", "33657", "99854") - -// val action = responseBody.action -// action shouldBe a[ShowNotificationAction] - - } - - Scenario("Type ahead request for a column") { - - Given("a view port exist") - val viewPortId: String = createViewPort - - When("request typeahead for Account column") - val getTypeAheadRequest = createTypeAheadRequest(viewPortId, tableName, "Account") - val requestId = vuuClient.send(sessionId, tokenId, getTypeAheadRequest) - - Then("return top 10 unique values in that column") - val response = vuuClient.awaitForResponse(requestId) - - val responseBody = assertBodyIsInstanceOf[ViewPortRpcResponse](response) - responseBody.method shouldEqual "getUniqueFieldValues" - assertResponseReturns(responseBody, List("12355", "45321", "89564", "42262", "65879", "88875", "45897", "23564", "33657", "99854")) } - Scenario("Type ahead request that start with a string for a column") { + Scenario("Start with a specified string for a column") { Given("a view port exist") val viewPortId: String = createViewPort When("request typeahead for Name column with start string Tom") - val getTypeAheadRequest = createTypeAheadStartWithRequest(viewPortId,tableName, "Name", "Tom") + val getTypeAheadRequest = createTypeAheadStartWithRequest(viewPortId, tableName, "Name", "Tom") val requestId = vuuClient.send(sessionId, tokenId, getTypeAheadRequest) Then("return all Name values that start with Tom") val response = vuuClient.awaitForResponse(requestId) - val responseBody = assertBodyIsInstanceOf[ViewPortRpcResponse](response) - responseBody.method shouldEqual "getUniqueFieldValuesStartingWith" - assertResponseReturns(responseBody, List("Tom Sawyer", "Tom Thatcher")) + val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) + responseBody.rpcName shouldEqual "getUniqueFieldValuesStartingWith" + responseBody.result.isSuccess shouldEqual true + responseBody.result.data shouldEqual List("Tom Sawyer", "Tom Thatcher") } - Scenario("Type ahead request with start with no matching value") { + Scenario("Start with a specified string that has no matching value") { Given("a view port exist") val viewPortId: String = createViewPort @@ -91,12 +69,13 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { Then("return success response with empty list") val response = vuuClient.awaitForResponse(requestId) - val responseBody = assertBodyIsInstanceOf[ViewPortRpcResponse](response) - responseBody.method shouldEqual "getUniqueFieldValuesStartingWith" - assertResponseReturns(responseBody, List.empty) + val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) + responseBody.rpcName shouldEqual "getUniqueFieldValuesStartingWith" + responseBody.result.isSuccess shouldEqual true + responseBody.result.data shouldEqual List.empty } - Scenario("Type ahead request for a column that is not in view port") { + Scenario("For a column that is not in view port") { Given("a view port exist") val viewPortId: String = createViewPort @@ -108,12 +87,13 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { Then("return success response with empty list") val response = vuuClient.awaitForResponse(requestId) - val responseBody = assertBodyIsInstanceOf[ViewPortRpcResponse](response) - responseBody.method shouldEqual "getUniqueFieldValues" - assertResponseReturns(responseBody, List.empty) + val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) + responseBody.rpcName shouldEqual "getUniqueFieldValues" + responseBody.result.isSuccess shouldEqual true + responseBody.result.data shouldEqual List.empty } - Scenario("Type ahead request for a column that does not exist") { + Scenario("For a column that does not exist") { Given("a view port exist") val viewPortId: String = createViewPort @@ -125,29 +105,36 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { Then("return success response with empty list") val response = vuuClient.awaitForResponse(requestId) - val responseBody = assertBodyIsInstanceOf[ViewPortRpcResponse](response) - responseBody.method shouldEqual "getUniqueFieldValues" - assertResponseReturns(responseBody, List.empty) + val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) + responseBody.rpcName shouldEqual "getUniqueFieldValues" + responseBody.result.isSuccess shouldEqual true + responseBody.result.data shouldEqual List.empty } - Scenario("Type ahead request for a view port that does not exist") { + Scenario("For a viewport that does not exist") { - When("request typeahead for column that does not exist") + When("request typeahead for Account column") val getTypeAheadRequest = createTypeAheadRequest("viewPortThatDoesNotExist", tableName, "Account") val requestId = vuuClient.send(sessionId, tokenId, getTypeAheadRequest) Then("return helpful error response") val response = vuuClient.awaitForResponse(requestId) - val responseBody = assertBodyIsInstanceOf[ViewPortMenuRpcReject](response) + val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) responseBody.rpcName shouldEqual "getUniqueFieldValues" - responseBody.error shouldEqual "No viewport viewPortThatDoesNotExist found for RPC Call for getUniqueFieldValues" + + responseBody.result.isSuccess shouldEqual false + responseBody.result.errorMessage shouldEqual "No viewport viewPortThatDoesNotExist found for RPC Call for getUniqueFieldValues" + //todo responseBody.result.data should be(null) + + And("Show error notification action") + val action = assertAndCastAsInstanceOf[ShowNotificationAction](responseBody.action) + action.notificationType shouldEqual "Error" + action.title shouldEqual "Failed to process getUniqueFieldValues request" + action.message shouldEqual "No viewport viewPortThatDoesNotExist found for RPC Call for getUniqueFieldValues" } - //create multiple view ports - //check type ahead work on view port columns rather than table columns - //match response on request id? - Scenario("Type ahead request for empty table") { + Scenario("For an empty table") { Given("a view port exist") val viewPortId: String = createViewPort @@ -159,12 +146,13 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { Then("return success response with empty list") val response = vuuClient.awaitForResponse(requestId) - val responseBody = assertBodyIsInstanceOf[ViewPortRpcResponse](response) - responseBody.method shouldEqual "getUniqueFieldValues" - assertResponseReturns(responseBody, List.empty) + val responseBody = assertBodyIsInstanceOf[RpcResponseNew](response) + responseBody.rpcName shouldEqual "getUniqueFieldValues" + responseBody.result.isSuccess shouldEqual true + responseBody.result.data shouldEqual List.empty } - Scenario("Type ahead request when there is multiple viewports and multiple requests") { + Scenario("When there is multiple viewports and multiple requests") { Given("multiple view port exist") val viewPortId1: String = createViewPort @@ -181,10 +169,10 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { val response1 = vuuClient.awaitForResponse(requestId1) val response2 = vuuClient.awaitForResponse(requestId2) - val responseBody1 = assertBodyIsInstanceOf[ViewPortRpcResponse](response1) - val responseBody2 = assertBodyIsInstanceOf[ViewPortRpcResponse](response2) - assertResponseReturns(responseBody1, List("Sid Sawyer","Sally Phelps")) - assertResponseReturns(responseBody2, List("Tom Sawyer","Tom Thatcher")) + val responseBody1 = assertBodyIsInstanceOf[RpcResponseNew](response1) + val responseBody2 = assertBodyIsInstanceOf[RpcResponseNew](response2) + responseBody1.result.data shouldEqual List("Sid Sawyer", "Sally Phelps") + responseBody2.result.data shouldEqual List("Tom Sawyer", "Tom Thatcher") } } @@ -252,20 +240,7 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { viewPortId } - private def createTypeAheadRequest(viewPortId: String, tableName: String, columnName: String): ViewPortRpcCall = { - ViewPortRpcCall( - viewPortId, - RpcNames.UniqueFieldValuesRpc, - params = Array(), - namedParams = Map( - "table" -> tableName, - "module" -> moduleName, - "column" -> columnName - )) - } - - - private def createTypeAheadRequestNew(viewPortId: String, tableName: String, columnName: String): RpcRequest = { + private def createTypeAheadRequest(viewPortId: String, tableName: String, columnName: String): RpcRequest = { RpcRequest( RpcContext(viewPortId), RpcNames.UniqueFieldValuesRpc, @@ -276,24 +251,15 @@ class TypeAheadWSApiTest extends WebSocketApiTestBase { )) } - private def createTypeAheadStartWithRequest(viewPortId: String, tableName: String, columnName: String, startString: String): ViewPortRpcCall = { - ViewPortRpcCall( - viewPortId, + private def createTypeAheadStartWithRequest(viewPortId: String, tableName: String, columnName: String, startString: String): RpcRequest = { + RpcRequest( + RpcContext(viewPortId), RpcNames.UniqueFieldValuesStartWithRpc, - params = Array(), - namedParams = Map( + params = Map( "table" -> tableName, "module" -> moduleName, "column" -> columnName, "starts" -> startString )) } - - private def assertResponseReturns(response:ViewPortRpcResponse, expectedResult: Any) = { - val action = response.action - action shouldBe a[DisplayResultAction] - val displayResultAction = action.asInstanceOf[DisplayResultAction] - - displayResultAction.result shouldEqual expectedResult - } } diff --git a/vuu/src/test/scala/org/finos/vuu/wsapi/WebSocketApiTestBase.scala b/vuu/src/test/scala/org/finos/vuu/wsapi/WebSocketApiTestBase.scala index 928dcfae7..881254ddb 100644 --- a/vuu/src/test/scala/org/finos/vuu/wsapi/WebSocketApiTestBase.scala +++ b/vuu/src/test/scala/org/finos/vuu/wsapi/WebSocketApiTestBase.scala @@ -89,10 +89,14 @@ abstract class WebSocketApiTestBase extends AnyFeatureSpec with BeforeAndAfterAl protected def assertBodyIsInstanceOf[BodyType](response: Option[ViewServerMessage]): BodyType = { response.isDefined shouldBe true - assert(response.get.body.isInstanceOf[BodyType]) - val responseBody = response.get.body.asInstanceOf[BodyType] - responseBody + assertAndCastAsInstanceOf(response.get.body) } + def assertAndCastAsInstanceOf[T](data: Any): T = { + assert(data.isInstanceOf[T]) + data.asInstanceOf[T] + } + + }