diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala index eac0cfbab..5e63956bb 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketConstituentProvider.scala @@ -4,6 +4,7 @@ import org.finos.toolbox.lifecycle.LifecycleContainer import org.finos.toolbox.thread.RunOnceLifeCycleRunner import org.finos.toolbox.time.Clock import org.finos.vuu.core.module.basket.BasketConstants +import org.finos.vuu.core.module.basket.BasketModule.BasketConstituentColumnNames.{BasketId, Change, Description, LastTrade, Ric, RicBasketId, Side, Volume, Weighting} import org.finos.vuu.core.module.basket.csv.BasketLoader import org.finos.vuu.core.table.{DataTable, RowWithData} import org.finos.vuu.provider.DefaultProvider @@ -13,6 +14,16 @@ class BasketConstituentProvider(val table: DataTable)(implicit lifecycle: Lifecy private val runner = new RunOnceLifeCycleRunner("BasketConstituentProvider", runOnce) private val basketLoader = new BasketLoader() + private val ricCol = table.getTableDef.columnForName(Ric) + private val basketIdCol = table.getTableDef.columnForName(BasketId) + private val ricBasketIdCol = table.getTableDef.columnForName(RicBasketId) + private val lastTradeCol = table.getTableDef.columnForName(LastTrade) + private val changeCol = table.getTableDef.columnForName(Change) + private val weightingCol = table.getTableDef.columnForName(Weighting) + private val volumeCol = table.getTableDef.columnForName(Volume) + private val sideCol = table.getTableDef.columnForName(Side) + private val descCol = table.getTableDef.columnForName(Description) + lifecycle(this).dependsOn(runner) import org.finos.vuu.core.module.basket.BasketModule.BasketConstituentColumnNames._ @@ -23,10 +34,13 @@ class BasketConstituentProvider(val table: DataTable)(implicit lifecycle: Lifecy } def updateBasketConstituents(basketId: String): Unit = { + val list = basketLoader.loadConstituents(basketId) + list.foreach(row => { if (row.nonEmpty) { + val symbol = row("Symbol").asInstanceOf[String] val name = row("Name") val lastTrade = row("Last Trade") @@ -35,17 +49,20 @@ class BasketConstituentProvider(val table: DataTable)(implicit lifecycle: Lifecy val weighting = row("Weighting") val side = BasketConstants.Side.Buy val ricBasketId = symbol + "." + basketId - table.processUpdate(ricBasketId, RowWithData(ricBasketId, Map( - Ric -> symbol, - BasketId -> basketId, - RicBasketId -> ricBasketId, - LastTrade -> lastTrade, - Change -> change, - Weighting -> weighting, - Volume -> volume, - Description -> name, - Side -> side - )), clock.now()) + + val rowData = table.newRow(ricBasketId) + .setString(ricCol, symbol) + .setString(basketIdCol, basketId) + .setString(ricBasketIdCol, ricBasketId) + .setString(lastTradeCol, Option(lastTrade).getOrElse("").toString) + .setString(changeCol, Option(change).getOrElse("").toString) + .setDouble(weightingCol, weighting.asInstanceOf[Double]) + .setString(volumeCol, Option(volume).getOrElse("").toString) + .setString(descCol, name.toString) + .setString(sideCol, side) + .asRow + + table.processUpdate(rowData, clock.now()) } }) diff --git a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketProvider.scala b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketProvider.scala index 2708b45ac..135b6a66f 100644 --- a/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketProvider.scala +++ b/example/basket/src/main/scala/org/finos/vuu/core/module/basket/provider/BasketProvider.scala @@ -13,15 +13,24 @@ class BasketProvider(val table: DataTable)(implicit lifecycle: LifecycleContaine private val runner = new RunOnceLifeCycleRunner("BasketProvider", runOnce) private val basketLoader = new BasketLoader() + private val idColumn = table.columnForName(Id) + private val nameColumn = table.columnForName(Name) + lifecycle(this).dependsOn(runner) def runOnce(): Unit = { val data = basketLoader.loadBasketIds() + //reuse of the builder... + val builder = table.rowBuilder + data.foreach(id => { - table.processUpdate(id, RowWithData(id, Map( - Id -> id, - Name -> id - )), clock.now()) + table.processUpdate(id, + builder.setKey(id) + .setString(idColumn, id) + .setString(nameColumn, id) + //as row clears out the data from the builder + .asRow, + clock.now()) }) } override val lifecycleId: String = "org.finos.vuu.core.module.basket.provider.BasketProvider" diff --git a/example/rest-api/src/test/scala/org/finos/vuu/example/rest/provider/InstrumentsProviderTest.scala b/example/rest-api/src/test/scala/org/finos/vuu/example/rest/provider/InstrumentsProviderTest.scala index 5a3a3baf8..f288d734d 100644 --- a/example/rest-api/src/test/scala/org/finos/vuu/example/rest/provider/InstrumentsProviderTest.scala +++ b/example/rest-api/src/test/scala/org/finos/vuu/example/rest/provider/InstrumentsProviderTest.scala @@ -1,7 +1,7 @@ package org.finos.vuu.example.rest.provider import org.finos.toolbox.json.JsonUtil -import org.finos.vuu.core.table.{Columns, DataTable, RowWithData} +import org.finos.vuu.core.table.{Columns, DataTable, RowData, RowWithData} import org.finos.toolbox.time.{Clock, TestFriendlyClock} import org.finos.vuu.api.TableDef import org.finos.vuu.core.module.ModuleFactory.stringToString @@ -29,7 +29,7 @@ class InstrumentsProviderTest extends AnyFeatureSpec with Matchers with MockFact getInstrumentsProvider(mockBackend).doStart() - (mockTable.processUpdate _).verify(expectedRow.get(KEY_FIELD).toString, expectedRow, *).once + (mockTable.processUpdate(_: String, _: RowData, _ : Long) ).verify(expectedRow.get(KEY_FIELD).toString, expectedRow, *).once } Scenario("can correctly make an external call, parse response and update the table WHEN server responds with multiple instruments") { @@ -41,16 +41,17 @@ class InstrumentsProviderTest extends AnyFeatureSpec with Matchers with MockFact getInstrumentsProvider(mockBackend).doStart() - expectedRows.foreach(row => (mockTable.processUpdate _).verify(row.get(KEY_FIELD).toString, row, *).once) + expectedRows.foreach(row => (mockTable.processUpdate(_: String, _: RowData, _ : Long) ).verify(row.get(KEY_FIELD).toString, row, *).once) } + Scenario("skips updating table when response is not parsable") { val mockClientResponse = "Some body" val mockBackend = SyncBackendStub.whenAnyRequest.thenRespond(mockClientResponse) getInstrumentsProvider(mockBackend).doStart() - (mockTable.processUpdate _).verify(*, *, *).never + (mockTable.processUpdate(_: String, _: RowData, _ : Long) ).verify(*, *, *).never } Scenario("skips updating table when response errors") { @@ -58,7 +59,7 @@ class InstrumentsProviderTest extends AnyFeatureSpec with Matchers with MockFact getInstrumentsProvider(mockBackend).doStart() - (mockTable.processUpdate _).verify(*, *, *).never + (mockTable.processUpdate(_: String, _: RowData, _ : Long) ).verify(*, *, *).never } } diff --git a/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTable.scala b/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTable.scala index 137a27bce..3a46afbc4 100644 --- a/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTable.scala +++ b/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTable.scala @@ -4,7 +4,7 @@ import com.typesafe.scalalogging.StrictLogging import org.finos.toolbox.jmx.MetricsProvider import org.finos.toolbox.time.Clock import org.finos.vuu.api.SessionTableDef -import org.finos.vuu.core.table.{ColumnValueProvider, InMemSessionDataTable, RowWithData, TableData, TablePrimaryKeys} +import org.finos.vuu.core.table.{ColumnValueProvider, InMemSessionDataTable, RowData, RowWithData, TableData, TablePrimaryKeys} import org.finos.vuu.net.ClientSessionId import org.finos.vuu.provider.{JoinTableProvider, VirtualizedProvider} @@ -17,7 +17,7 @@ class VirtualizedSessionTable(clientSessionId: ClientSessionId, @volatile private var dataSetSize: Int = 0 @volatile private var range = VirtualizedRange(0, 0) - override def toString: String = s"VirtualizedSessionTable(tableDef=${sessionTableDef.name}, name=${name})" + override def toString: String = s"VirtualizedSessionTable(tableDef=${sessionTableDef.name}, name=$name)" override def primaryKeys: TablePrimaryKeys = super.primaryKeys @@ -55,7 +55,7 @@ class VirtualizedSessionTable(clientSessionId: ClientSessionId, logger.error("Trying to set range on non-virtualized data, something has gone bad.") } } - override def processUpdate(rowKey: String, rowData: RowWithData, timeStamp: Long): Unit = super.processUpdate(rowKey, rowData, timeStamp) + override def processUpdate(rowKey: String, rowData: RowData, timeStamp: Long): Unit = super.processUpdate(rowKey, rowData, timeStamp) override def processDelete(rowKey: String): Unit = super.processDelete(rowKey) diff --git a/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTableData.scala b/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTableData.scala index d098ea294..487916303 100644 --- a/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTableData.scala +++ b/plugin/virtualized-table-plugin/src/main/scala/org/finos/vuu/plugin/virtualized/table/VirtualizedSessionTableData.scala @@ -18,7 +18,7 @@ class VirtualizedSessionTableData(cacheSize: Int)(implicit clock: Clock) extends } } - override def update(key: String, update: RowWithData): TableData = { + override def update(key: String, update: RowData): TableData = { rowCache.put(key, update) this } diff --git a/vuu/src/main/scala/org/finos/vuu/core/row/InMemMapRowBuilder.scala b/vuu/src/main/scala/org/finos/vuu/core/row/InMemMapRowBuilder.scala new file mode 100644 index 000000000..f52662bf5 --- /dev/null +++ b/vuu/src/main/scala/org/finos/vuu/core/row/InMemMapRowBuilder.scala @@ -0,0 +1,48 @@ +package org.finos.vuu.core.row +import org.finos.vuu.core.table.{Column, RowData, RowWithData} + +import scala.collection.mutable + +class InMemMapRowBuilder extends RowBuilder { + + private val mutableMap = new mutable.HashMap[String, Any]() + private var key: String = null + override def setLong(column: Column, v: Long): RowBuilder = { + mutableMap.put(column.name, v) + this + } + + override def setDouble(column: Column, v: Double): RowBuilder = { + mutableMap.put(column.name, v) + this + } + + override def setInt(column: Column, v: Int): RowBuilder = { + mutableMap.put(column.name, v) + this + } + + override def setString(column: Column, v: String): RowBuilder = { + mutableMap.put(column.name, v) + this + } + + override def setBoolean(column: Column, v: Boolean): RowBuilder = { + mutableMap.put(column.name, v) + this + } + override def setKey(key: String): RowBuilder = { + this.key = key + this + } + override def asRow: RowData = { + if(key == null){ + throw new RuntimeException("Key has not been set, this is likely a coding error.") + } + val immMap = mutableMap.toMap + val rowData = RowWithData(key, immMap) + mutableMap.clear() + key = null + rowData + } +} diff --git a/vuu/src/main/scala/org/finos/vuu/core/row/RowBuilder.scala b/vuu/src/main/scala/org/finos/vuu/core/row/RowBuilder.scala new file mode 100644 index 000000000..48da0d986 --- /dev/null +++ b/vuu/src/main/scala/org/finos/vuu/core/row/RowBuilder.scala @@ -0,0 +1,32 @@ +package org.finos.vuu.core.row + +import org.finos.vuu.core.table.{Column, RowData} + +trait RowBuilder { + def setKey(key: String): RowBuilder + def setLong(column: Column, v: Long): RowBuilder + def setDouble(column: Column, v: Double): RowBuilder + def setInt(column: Column, v: Int): RowBuilder + def setString(column: Column, v: String): RowBuilder + def setBoolean(column: Column, v: Boolean): RowBuilder + /** + * this metyhod effectively resets the builder, emptying its existing contents to begin again. + * @return row with data set + */ + def asRow: RowData +} + +object NoRowBuilder extends RowBuilder{ + override def setKey(key: String): RowBuilder = ??? + override def setLong(column: Column, v: Long): RowBuilder = ??? + override def setDouble(column: Column, v: Double): RowBuilder = ??? + override def setInt(column: Column, v: Int): RowBuilder = ??? + override def setString(column: Column, v: String): RowBuilder = ??? + override def setBoolean(column: Column, v: Boolean): RowBuilder = ??? + /** + * this metyhod effectively resets the builder, emptying its existing contents to begin again. + * + * @return row with data set + */ + override def asRow: RowData = ??? +} diff --git a/vuu/src/main/scala/org/finos/vuu/core/table/AutoSubscribeTable.scala b/vuu/src/main/scala/org/finos/vuu/core/table/AutoSubscribeTable.scala index 2643544b3..72caae686 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/table/AutoSubscribeTable.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/table/AutoSubscribeTable.scala @@ -5,6 +5,7 @@ import org.finos.vuu.api.TableDef import org.finos.vuu.provider.JoinTableProvider import io.vertx.core.impl.ConcurrentHashSet import org.finos.toolbox.jmx.MetricsProvider +import org.finos.vuu.core.row.RowBuilder class AutoSubscribeTable(tableDef: TableDef, joinProvider: JoinTableProvider)(implicit override val metrics: MetricsProvider) extends InMemDataTable(tableDef, joinProvider) with StrictLogging { diff --git a/vuu/src/main/scala/org/finos/vuu/core/table/InMemDataTable.scala b/vuu/src/main/scala/org/finos/vuu/core/table/InMemDataTable.scala index 1887358b8..9e336a993 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/table/InMemDataTable.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/table/InMemDataTable.scala @@ -7,6 +7,7 @@ import org.finos.vuu.viewport.{RowProcessor, RowSource, ViewPortColumns} import org.finos.toolbox.collection.array.ImmutableArray import org.finos.toolbox.jmx.MetricsProvider import org.finos.toolbox.text.AsciiUtil +import org.finos.vuu.core.row.{InMemMapRowBuilder, RowBuilder} import org.finos.vuu.feature.inmem.InMemTablePrimaryKeys import java.util @@ -22,6 +23,8 @@ trait DataTable extends KeyedObservable[RowKeyUpdate] with RowSource { def updateCounter: Long + def newRow(key: String): RowBuilder + def rowBuilder: RowBuilder def incrementUpdateCounter(): Unit def indexForColumn(column: Column): Option[IndexedField[_]] @@ -42,7 +45,11 @@ trait DataTable extends KeyedObservable[RowKeyUpdate] with RowSource { def getTableDef: TableDef - def processUpdate(rowKey: String, rowUpdate: RowWithData, timeStamp: Long): Unit + def processUpdate(rowUpdate: RowData, timeStamp: Long): Unit = { + processUpdate(rowUpdate.key(), rowUpdate, timeStamp) + } + + def processUpdate(rowKey: String, rowUpdate: RowData, timeStamp: Long): Unit def hasRowChanged(row: RowWithData): Boolean = { val existingRow = this.pullRow(row.key) @@ -170,10 +177,10 @@ case class InMemDataTableData(data: ConcurrentHashMap[String, RowData], private //protected def merge(update: RowUpdate, data: RowData): RowData = MergeFunctions.mergeLeftToRight(update, data) - protected def merge(update: RowWithData, data: RowWithData): RowWithData = + protected def merge(update: RowData, data: RowData): RowData = MergeFunctions.mergeLeftToRight(update, data) - def update(key: String, update: RowWithData): TableData = { + def update(key: String, update: RowData): TableData = { val table = data.synchronized { @@ -220,6 +227,11 @@ class InMemDataTable(val tableDef: TableDef, val joinProvider: JoinTableProvider private final val columnValueProvider = InMemColumnValueProvider(this) + override def newRow(key: String): RowBuilder = { + new InMemMapRowBuilder().setKey(key) + } + override def rowBuilder: RowBuilder = new InMemMapRowBuilder + private def buildIndexForColumn(c: Column): IndexedField[_] = { c.dataType match { case DataType.StringDataType => @@ -333,7 +345,7 @@ class InMemDataTable(val tableDef: TableDef, val joinProvider: JoinTableProvider def columns(): Array[Column] = tableDef.columns lazy val viewPortColumns: ViewPortColumns = ViewPortColumnCreator.create(this, tableDef.columns.map(_.name).toList) - private def updateIndices(rowkey: String, rowUpdate: RowWithData): Unit = { + private def updateIndices(rowkey: String, rowUpdate: RowData): Unit = { this.indices.foreach(colTup => { val column = colTup._1 val index = colTup._2 @@ -375,7 +387,7 @@ class InMemDataTable(val tableDef: TableDef, val joinProvider: JoinTableProvider }) } - def update(rowkey: String, rowUpdate: RowWithData): Unit = { + def update(rowkey: String, rowUpdate: RowData): Unit = { data = data.update(rowkey, rowUpdate) updateIndices(rowkey, rowUpdate) } @@ -446,7 +458,7 @@ class InMemDataTable(val tableDef: TableDef, val joinProvider: JoinTableProvider } } - def processUpdate(rowKey: String, rowData: RowWithData, timeStamp: Long): Unit = { + def processUpdate(rowKey: String, rowData: RowData, timeStamp: Long): Unit = { onUpdateMeter.mark() diff --git a/vuu/src/main/scala/org/finos/vuu/core/table/JoinTable.scala b/vuu/src/main/scala/org/finos/vuu/core/table/JoinTable.scala index c85c3aa41..fc80b1294 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/table/JoinTable.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/table/JoinTable.scala @@ -7,6 +7,7 @@ import org.finos.vuu.provider.JoinTableProvider import org.finos.vuu.viewport.{RowProcessor, ViewPortColumns} import org.finos.toolbox.collection.array.{ImmutableArray, ImmutableArrays} import org.finos.toolbox.jmx.MetricsProvider +import org.finos.vuu.core.row.{NoRowBuilder, RowBuilder} import org.finos.vuu.feature.inmem.InMemTablePrimaryKeys import java.util @@ -104,7 +105,7 @@ case class JoinDataTableData(tableDef: JoinTableDef, var keysByJoinIndex: Array[ map.toMap } - def rowUpdateToArray(update: RowWithData): Array[Any] = { + def rowUpdateToArray(update: RowData): Array[Any] = { //val data = columns.map(update.get(_)) var index = 0 @@ -189,7 +190,7 @@ case class JoinDataTableData(tableDef: JoinTableDef, var keysByJoinIndex: Array[ } } - def processUpdate(rowKey: String, rowUpdate: RowWithData, joinTable: JoinTable, sourceTables: Map[String, DataTable]): JoinDataTableData = { + def processUpdate(rowKey: String, rowUpdate: RowData, joinTable: JoinTable, sourceTables: Map[String, DataTable]): JoinDataTableData = { val updateByKeyIndex = rowUpdateToArray(rowUpdate) @@ -347,7 +348,7 @@ class JoinTable(val tableDef: JoinTableDef, val sourceTables: Map[String, DataTa override def incrementUpdateCounter(): Unit = updateCounterInternal +=1 - override def processUpdate(rowKey: String, rowUpdate: RowWithData, timeStamp: Long): Unit = { + override def processUpdate(rowKey: String, rowUpdate: RowData, timeStamp: Long): Unit = { onUpdateMeter.mark() @@ -658,4 +659,7 @@ class JoinTable(val tableDef: JoinTableDef, val sourceTables: Map[String, DataTa } override def getColumnValueProvider: ColumnValueProvider = InMemColumnValueProvider(this) + override def newRow(key: String): RowBuilder = ??? + + override def rowBuilder: RowBuilder = NoRowBuilder } diff --git a/vuu/src/main/scala/org/finos/vuu/core/table/MergeFunctions.scala b/vuu/src/main/scala/org/finos/vuu/core/table/MergeFunctions.scala index eedcb6190..ac76471f2 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/table/MergeFunctions.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/table/MergeFunctions.scala @@ -2,15 +2,16 @@ package org.finos.vuu.core.table object MergeFunctions { - def mergeLeftToRight(update: RowWithData, data: RowWithData): RowWithData = { + def mergeLeftToRight(update: RowData, data: RowData): RowData = { - assert(update.key == data.key, s"check we're updating the same row ${update.key} != ${data.key}") + assert(update.key == data.key, s"check we're updating the same row ${update.key()} != ${data.key()}") - var newData: RowWithData = data + var newData: RowData = data - update.data.foreach({ case (field, value) => newData = newData.set(field, value) }) - - //println(">" + newData) + update match { + case update: RowWithData => + update.data.foreach({ case (field, value) => newData = newData.set(field, value) }) + } newData } diff --git a/vuu/src/main/scala/org/finos/vuu/core/table/TableData.scala b/vuu/src/main/scala/org/finos/vuu/core/table/TableData.scala index 2371b36c6..e85db6efd 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/table/TableData.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/table/TableData.scala @@ -4,7 +4,7 @@ import org.finos.toolbox.collection.array.ImmutableArray trait TableData { def dataByKey(key: String): RowData - def update(key: String, update: RowWithData): TableData + def update(key: String, update: RowData): TableData def delete(key: String): TableData def deleteAll(): TableData def primaryKeyValues: TablePrimaryKeys diff --git a/vuu/src/main/scala/org/finos/vuu/core/tree/TreeSessionTableImpl.scala b/vuu/src/main/scala/org/finos/vuu/core/tree/TreeSessionTableImpl.scala index 8835b4ae4..c5d1a7196 100644 --- a/vuu/src/main/scala/org/finos/vuu/core/tree/TreeSessionTableImpl.scala +++ b/vuu/src/main/scala/org/finos/vuu/core/tree/TreeSessionTableImpl.scala @@ -78,7 +78,7 @@ class TreeSessionTableImpl(val source: RowSource, val session: ClientSessionId, onRowDeleteFn = fn } - override def processUpdate(rowKey: String, rowData: RowWithData, timeStamp: Long): Unit = { + override def processUpdate(rowKey: String, rowData: RowData, timeStamp: Long): Unit = { logger.debug(s"ChrisChris>> GroupBySession processUpdate $rowKey $rowData") super.processUpdate(rowKey, rowData, timeStamp) incrementUpdateCounter() diff --git a/vuu/src/test/scala/org/finos/vuu/core/module/metrics/MetricsTableProviderTest.scala b/vuu/src/test/scala/org/finos/vuu/core/module/metrics/MetricsTableProviderTest.scala index 2977cfa5a..fadbc57a9 100644 --- a/vuu/src/test/scala/org/finos/vuu/core/module/metrics/MetricsTableProviderTest.scala +++ b/vuu/src/test/scala/org/finos/vuu/core/module/metrics/MetricsTableProviderTest.scala @@ -3,7 +3,7 @@ package org.finos.vuu.core.module.metrics import org.finos.toolbox.jmx.{MetricsProvider, MetricsProviderImpl} import org.finos.toolbox.lifecycle.{LifeCycleComponentContext, LifecycleContainer, LifecycleEnabled} import org.finos.toolbox.time.{Clock, TestFriendlyClock} -import org.finos.vuu.core.table.{DataTable, TableContainer} +import org.finos.vuu.core.table.{DataTable, RowData, TableContainer} import org.finos.vuu.core.table.TableMockFactory._ import org.finos.vuu.test.TestFriendlyJoinTableProvider import org.scalamock.scalatest.MockFactory @@ -34,10 +34,10 @@ class MetricsTableProviderTest extends AnyFeatureSpec with Matchers with MockFac metricsTableProvider.runOnce() - (mockTable.processUpdate _).verify("instrumentsSessionTable_1", *, *).once - (mockTable.processUpdate _).verify("instrumentsSessionTable_2", *, *).once - (mockTable.processUpdate _).verify("fills_table", *, *).once - (mockTable.processUpdate _).verify("other", *, *).once + (mockTable.processUpdate(_: String, _: RowData, _: Long)).verify("instrumentsSessionTable_1", *, *).once + (mockTable.processUpdate(_: String, _: RowData, _: Long)).verify("instrumentsSessionTable_2", *, *).once + (mockTable.processUpdate(_: String, _: RowData, _: Long)).verify("fills_table", *, *).once + (mockTable.processUpdate(_: String, _: RowData, _: Long)).verify("other", *, *).once } } } diff --git a/vuu/src/test/scala/org/finos/vuu/core/row/RowBuilderTest.scala b/vuu/src/test/scala/org/finos/vuu/core/row/RowBuilderTest.scala new file mode 100644 index 000000000..5a00b80c0 --- /dev/null +++ b/vuu/src/test/scala/org/finos/vuu/core/row/RowBuilderTest.scala @@ -0,0 +1,120 @@ +package org.finos.vuu.core.row + +import org.finos.toolbox.jmx.MetricsProviderImpl +import org.finos.toolbox.lifecycle.LifecycleContainer +import org.finos.toolbox.time.{Clock, TestFriendlyClock} +import org.finos.vuu.api.TableDef +import org.finos.vuu.core.table.{Columns, InMemDataTable} +import org.finos.vuu.provider.VuuJoinTableProvider +import org.scalatest.GivenWhenThen +import org.scalatest.featurespec.AnyFeatureSpec +import org.scalatest.matchers.should.Matchers + +class RowBuilderTest extends AnyFeatureSpec with Matchers with GivenWhenThen{ + + def getTableDef: TableDef = { + TableDef( + name = "instruments", + keyField = "ric", + columns = Columns.fromNames("ric:String", "description:String", "currency: String", "exchange:String", "lotSize:Double"), + joinFields = "ric" + ) + } + + Feature("Test the row Builder for use in providers"){ + + Scenario("Test New Each Time Row Builder"){ + + implicit val clock: Clock = new TestFriendlyClock(1000000) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer + implicit val metrics: MetricsProviderImpl = new MetricsProviderImpl + + val tableDef = getTableDef + val joinTableProvider: VuuJoinTableProvider = new VuuJoinTableProvider() + + val (ricColumn, descColumn, currColumn, exchangeColumn, lotSizeColumn) = + ( + tableDef.columnForName("ric"), tableDef.columnForName("description"), + tableDef.columnForName("currency"),tableDef.columnForName("exchange"), tableDef.columnForName("lotSize") + ) + + val table = new InMemDataTable(tableDef, joinTableProvider) + + val row = table.rowBuilder + .setKey("FOO.L") + .setString(ricColumn, "FOO.L") + .setString(descColumn, "Foo Inc") + .setString(currColumn, "GBP") + .setString(exchangeColumn, "LSE") + .setDouble(lotSizeColumn, 1000.123) + .asRow + + row.key() should equal("FOO.L") + row.get(ricColumn) should equal("FOO.L") + row.get(descColumn) should equal("Foo Inc") + row.get(currColumn) should equal("GBP") + row.get(exchangeColumn) should equal("LSE") + row.get(lotSizeColumn) should equal(1000.123) + + } + + Scenario("Test Reuse of Row Builder"){ + + implicit val clock: Clock = new TestFriendlyClock(1000000) + implicit val lifecycle: LifecycleContainer = new LifecycleContainer + implicit val metrics: MetricsProviderImpl = new MetricsProviderImpl + + val tableDef = getTableDef + val joinTableProvider: VuuJoinTableProvider = new VuuJoinTableProvider() + + val (ricColumn, descColumn, currColumn, exchangeColumn, lotSizeColumn) = + ( + tableDef.columnForName("ric"), tableDef.columnForName("description"), + tableDef.columnForName("currency"),tableDef.columnForName("exchange"), tableDef.columnForName("lotSize") + ) + + val table = new InMemDataTable(tableDef, joinTableProvider) + + val builder = table.rowBuilder + + val row = builder.setKey("FOO.L") + .setString(ricColumn, "FOO.L") + .setString(descColumn, "Foo Inc") + .setString(currColumn, "GBP") + .setString(exchangeColumn, "LSE") + .setDouble(lotSizeColumn, 1000.123) + .asRow + + row.key() should equal("FOO.L") + row.get(ricColumn) should equal("FOO.L") + row.get(descColumn) should equal("Foo Inc") + row.get(currColumn) should equal("GBP") + row.get(exchangeColumn) should equal("LSE") + row.get(lotSizeColumn) should equal(1000.123) + + intercept[RuntimeException]{ + builder.asRow + } + + val row2 = builder.setKey("BAR.L") + .setString(ricColumn, "BAR.L") + .setString(descColumn, "Bar Inc") + .setString(currColumn, "USD") + .setString(exchangeColumn, "NYSE") + .setDouble(lotSizeColumn, 1010.123) + .asRow + + row2.key() should equal("BAR.L") + row2.get(ricColumn) should equal("BAR.L") + row2.get(descColumn) should equal("Bar Inc") + row2.get(currColumn) should equal("USD") + row2.get(exchangeColumn) should equal("NYSE") + row2.get(lotSizeColumn) should equal(1010.123) + + } + + + } + + +} diff --git a/vuu/src/test/scala/org/finos/vuu/test/FakeInMemoryTable.scala b/vuu/src/test/scala/org/finos/vuu/test/FakeInMemoryTable.scala index 5df637338..931cdec00 100644 --- a/vuu/src/test/scala/org/finos/vuu/test/FakeInMemoryTable.scala +++ b/vuu/src/test/scala/org/finos/vuu/test/FakeInMemoryTable.scala @@ -2,23 +2,27 @@ package org.finos.vuu.test import org.finos.vuu.api.TableDef import org.finos.vuu.core.index.IndexedField +import org.finos.vuu.core.row.RowBuilder import org.finos.vuu.core.table.{Column, ColumnValueProvider, DataTable, KeyObserver, RowData, RowKeyUpdate, RowWithData, TableData, TablePrimaryKeys} import org.finos.vuu.viewport.{RowProcessor, ViewPortColumns} class FakeInMemoryTable(val instanceName: String, val tableDef: TableDef) extends DataTable { - private val rowMap = scala.collection.mutable.HashMap.empty[String, RowWithData] + private val rowMap = scala.collection.mutable.HashMap.empty[String, RowData] override def name: String = instanceName override def getTableDef: TableDef = tableDef - override def processUpdate(rowKey: String, rowUpdate: RowWithData, timeStamp: Long): Unit = + override def newRow(key: String): RowBuilder = ??? + override def rowBuilder: RowBuilder = ??? + + override def processUpdate(rowKey: String, rowUpdate: RowData, timeStamp: Long): Unit = rowMap += (rowKey -> rowUpdate) override def pullRow(key: String): RowData = rowMap.getOrElse(key, throw new Exception(s"Could not find row data for key $key in table $name")) - def pullAllRows() : List[RowWithData] = rowMap.values.toList + def pullAllRows() : List[RowData] = rowMap.values.toList override protected def createDataTableData(): TableData = ???