From 1894ebce6aa886a58a3f3c5b2308423818430377 Mon Sep 17 00:00:00 2001 From: Manfred Zingl Date: Wed, 11 Oct 2023 18:14:14 +0200 Subject: [PATCH] add preparation for row permission insert on create row --- .../controller/SystemController.scala | 3 +- .../controller/TableauxController.scala | 13 +++- .../database/model/HistoryModel.scala | 4 +- .../database/model/TableauxModel.scala | 16 ++-- .../database/model/tableaux/RowModel.scala | 32 +++++--- .../campudus/tableaux/helper/JsonUtils.scala | 16 +++- .../tableaux/router/TableauxRouter.scala | 7 +- .../CreateRowWithRowPermissionsTest.scala | 74 +++++++++++++++++++ 8 files changed, 137 insertions(+), 28 deletions(-) create mode 100644 src/test/scala/com/campudus/tableaux/api/permission/CreateRowWithRowPermissionsTest.scala diff --git a/src/main/scala/com/campudus/tableaux/controller/SystemController.scala b/src/main/scala/com/campudus/tableaux/controller/SystemController.scala index f307f134..4d9bf91b 100644 --- a/src/main/scala/com/campudus/tableaux/controller/SystemController.scala +++ b/src/main/scala/com/campudus/tableaux/controller/SystemController.scala @@ -185,7 +185,8 @@ class SystemController( columnIds = columns.map(_.id) rowsWithColumnIdAndValue = rows.map(columnIds.zip(_)) - _ <- tableauxModel.createRows(table, rowsWithColumnIdAndValue) + // don't pass row permissions here, because method is only used for demo data + _ <- tableauxModel.createRows(table, rowsWithColumnIdAndValue, None) } yield table } diff --git a/src/main/scala/com/campudus/tableaux/controller/TableauxController.scala b/src/main/scala/com/campudus/tableaux/controller/TableauxController.scala index 083c8231..109f0138 100644 --- a/src/main/scala/com/campudus/tableaux/controller/TableauxController.scala +++ b/src/main/scala/com/campudus/tableaux/controller/TableauxController.scala @@ -242,7 +242,11 @@ class TableauxController( } } - def createRow(tableId: TableId, values: Option[Seq[Seq[(ColumnId, _)]]])( + def createRow( + tableId: TableId, + values: Option[Seq[Seq[(ColumnId, _)]]], + rowPermissionsOpt: Option[Seq[String]] = None + )( implicit user: TableauxUser ): Future[DomainObject] = { checkArguments(greaterZero(tableId)) @@ -254,10 +258,10 @@ class TableauxController( case Some(seq) => checkArguments(nonEmpty(seq, "rows")) logger.info(s"createRows ${table.id} $values") - repository.createRows(table, seq) + repository.createRows(table, seq, rowPermissionsOpt) case None => logger.info(s"createRow ${table.id}") - repository.createRow(table) + repository.createRow(table, rowPermissionsOpt) } } yield row } @@ -529,7 +533,8 @@ class TableauxController( } table <- repository.createTable(tableName, hidden = false) columnIds <- repository.createColumns(table, columns).map(_.map(_.id)) - _ <- repository.createRows(table, rows.map(columnIds.zip(_))) + // TODO: pass row permissions to createRows + _ <- repository.createRows(table, rows.map(columnIds.zip(_)), None) completeTable <- retrieveCompleteTable(table.id) } yield completeTable } diff --git a/src/main/scala/com/campudus/tableaux/database/model/HistoryModel.scala b/src/main/scala/com/campudus/tableaux/database/model/HistoryModel.scala index c656a043..8d210103 100644 --- a/src/main/scala/com/campudus/tableaux/database/model/HistoryModel.scala +++ b/src/main/scala/com/campudus/tableaux/database/model/HistoryModel.scala @@ -900,8 +900,10 @@ case class CreateHistoryModel(tableauxModel: TableauxModel, connection: Database } } - def createRow(table: Table, rowId: RowId)(implicit user: TableauxUser): Future[RowId] = { + def createRow(table: Table, rowId: RowId, rowPermissionsOpt: Option[Seq[String]])(implicit + user: TableauxUser): Future[RowId] = { insertCreateRowHistory(table, rowId) + // TODO create history row with row permissions } def deleteRow(table: Table, rowId: RowId, replacingRowIdOpt: Option[Int])( diff --git a/src/main/scala/com/campudus/tableaux/database/model/TableauxModel.scala b/src/main/scala/com/campudus/tableaux/database/model/TableauxModel.scala index c4ab733c..1c7b0fa2 100644 --- a/src/main/scala/com/campudus/tableaux/database/model/TableauxModel.scala +++ b/src/main/scala/com/campudus/tableaux/database/model/TableauxModel.scala @@ -357,15 +357,15 @@ class TableauxModel( } - def createRow(table: Table)(implicit user: TableauxUser): Future[Row] = { + def createRow(table: Table, rowPermissionsOpt: Option[Seq[String]])(implicit user: TableauxUser): Future[Row] = { for { - rowId <- createRowModel.createRow(table, Seq.empty) - _ <- createHistoryModel.createRow(table, rowId) + rowId <- createRowModel.createRow(table, Seq.empty, rowPermissionsOpt) + _ <- createHistoryModel.createRow(table, rowId, rowPermissionsOpt) row <- retrieveRow(table, rowId) } yield row } - def createRows(table: Table, rows: Seq[Seq[(ColumnId, Any)]])( + def createRows(table: Table, rows: Seq[Seq[(ColumnId, Any)]], rowPermissionsOpt: Option[Seq[String]])( implicit user: TableauxUser ): Future[RowSeq] = { for { @@ -396,8 +396,8 @@ class TableauxModel( case _ => Future.successful(()) } - rowId <- createRowModel.createRow(table, columnValuePairs) - _ <- createHistoryModel.createRow(table, rowId) + rowId <- createRowModel.createRow(table, columnValuePairs, rowPermissionsOpt) + _ <- createHistoryModel.createRow(table, rowId, rowPermissionsOpt) _ <- createHistoryModel.createCells(table, rowId, columnValuePairs) newRow <- retrieveRow(table, columns, rowId) @@ -1175,7 +1175,7 @@ class TableauxModel( rowValues = row.values // First create a empty row - duplicatedRowId <- createRowModel.createRow(table, Seq.empty) + duplicatedRowId <- createRowModel.createRow(table, Seq.empty, Option(row.rowPermissions.value)) // Fill the row with life _ <- updateRowModel @@ -1187,7 +1187,7 @@ class TableauxModel( .flatMap(_ => Future.failed(ex)) }) - _ <- createHistoryModel.createRow(table, duplicatedRowId) + _ <- createHistoryModel.createRow(table, duplicatedRowId, Option(row.rowPermissions.value)) _ <- createHistoryModel.createCells(table, rowId, columns.zip(rowValues)) // Retrieve duplicated row with all columns diff --git a/src/main/scala/com/campudus/tableaux/database/model/tableaux/RowModel.scala b/src/main/scala/com/campudus/tableaux/database/model/tableaux/RowModel.scala index 7ed03466..e7a83c0a 100644 --- a/src/main/scala/com/campudus/tableaux/database/model/tableaux/RowModel.scala +++ b/src/main/scala/com/campudus/tableaux/database/model/tableaux/RowModel.scala @@ -118,6 +118,7 @@ sealed trait UpdateCreateRowModelHelper extends LazyLogging { table: Table, rowId: RowId, values: Seq[(LinkColumn, Seq[RowId])], + rowPermissionsOpt: Option[Seq[String]], maybeTransaction: Option[DbTransaction] = None )(implicit ec: ExecutionContext): Future[Unit] = { val func = (value: (LinkColumn, Seq[RowId])) => { @@ -498,7 +499,7 @@ class UpdateRowModel(val connection: DatabaseConnection) extends DatabaseQuery w for { _ <- if (simple.isEmpty) Future.successful(()) else updateSimple(table, rowId, simple) _ <- if (multis.isEmpty) Future.successful(()) else updateTranslations(table, rowId, multis) - _ <- if (links.isEmpty) Future.successful(()) else updateLinks(table, rowId, links, maybeTransaction) + _ <- if (links.isEmpty) Future.successful(()) else updateLinks(table, rowId, links, None, maybeTransaction) _ <- if (attachments.isEmpty) Future.successful(()) else updateAttachments(table, rowId, attachments) } yield () } @@ -755,7 +756,11 @@ class UpdateRowModel(val connection: DatabaseConnection) extends DatabaseQuery w class CreateRowModel(val connection: DatabaseConnection) extends DatabaseQuery with UpdateCreateRowModelHelper { val attachmentModel = AttachmentModel(connection) - def createRow(table: Table, values: Seq[(ColumnType[_], _)]): Future[RowId] = { + def createRow( + table: Table, + values: Seq[(ColumnType[_], _)], + rowPermissionsOpt: Option[Seq[String]] + ): Future[RowId] = { val tableId = table.id ColumnType.splitIntoTypesWithValues(values) match { case Failure(ex) => @@ -763,10 +768,13 @@ class CreateRowModel(val connection: DatabaseConnection) extends DatabaseQuery w case Success((simple, multis, links, attachments)) => for { - rowId <- if (simple.isEmpty) createEmpty(tableId) else createSimple(tableId, simple) - _ <- if (multis.isEmpty) Future.successful(()) else createTranslations(tableId, rowId, multis) - _ <- if (links.isEmpty) Future.successful(()) else updateLinks(table, rowId, links) - _ <- if (attachments.isEmpty) Future.successful(()) else createAttachments(tableId, rowId, attachments) + rowId <- if (simple.isEmpty) createEmpty(tableId) else createSimple(tableId, simple, rowPermissionsOpt) + _ <- + if (multis.isEmpty) Future.successful(()) else createTranslations(tableId, rowId, multis, rowPermissionsOpt) + _ <- if (links.isEmpty) Future.successful(()) else updateLinks(table, rowId, links, rowPermissionsOpt) + _ <- + if (attachments.isEmpty) Future.successful(()) + else createAttachments(tableId, rowId, attachments, rowPermissionsOpt) } yield rowId } } @@ -779,7 +787,11 @@ class CreateRowModel(val connection: DatabaseConnection) extends DatabaseQuery w } } - private def createSimple(tableId: TableId, values: Seq[(SimpleValueColumn[_], Option[Any])]): Future[RowId] = { + private def createSimple( + tableId: TableId, + values: Seq[(SimpleValueColumn[_], Option[Any])], + rowPermissionsOpt: Option[Seq[String]] + ): Future[RowId] = { val placeholder = values.map(_ => "?").mkString(", ") val columns = values.map({ case (column: ColumnType[_], _) => s"column_${column.id}" }).mkString(", ") val binds = values.map({ case (_, value) => value.orNull }) @@ -795,7 +807,8 @@ class CreateRowModel(val connection: DatabaseConnection) extends DatabaseQuery w private def createTranslations( tableId: TableId, rowId: RowId, - values: Seq[(SimpleValueColumn[_], Map[String, Option[_]])] + values: Seq[(SimpleValueColumn[_], Map[String, Option[_]])], + rowPermissionsOpt: Option[Seq[String]] ): Future[_] = { val entries = for { (column, langtagValueOptMap) <- values @@ -826,7 +839,8 @@ class CreateRowModel(val connection: DatabaseConnection) extends DatabaseQuery w private def createAttachments( tableId: TableId, rowId: RowId, - values: Seq[(AttachmentColumn, Seq[(UUID, Option[Ordering])])] + values: Seq[(AttachmentColumn, Seq[(UUID, Option[Ordering])])], + rowPermissionsOpt: Option[Seq[String]] ): Future[_] = { val futureSequence = for { (column: AttachmentColumn, attachmentValue) <- values diff --git a/src/main/scala/com/campudus/tableaux/helper/JsonUtils.scala b/src/main/scala/com/campudus/tableaux/helper/JsonUtils.scala index d313e15e..f4d453ba 100644 --- a/src/main/scala/com/campudus/tableaux/helper/JsonUtils.scala +++ b/src/main/scala/com/campudus/tableaux/helper/JsonUtils.scala @@ -367,6 +367,20 @@ object JsonUtils extends LazyLogging { } } + def getRowPermissionsOpt(json: JsonObject): Option[Seq[String]] = { + val rowPermissionsOpt = booleanToValueOption( + json.containsKey("rowPermissions"), { + checkAllValuesOfArray[String]( + json.getJsonArray("rowPermissions"), + d => d.isInstanceOf[String], + "rowPermissions" + ).get + } + ).map(_.asScala.toSeq.map({ case perm: String => perm })) + + rowPermissionsOpt + } + def toLocationType(json: JsonObject): LocationType = { (for { location <- notNull(json.getString("location"), "location") @@ -403,7 +417,7 @@ object JsonUtils extends LazyLogging { * Helper to cast a Json Array to a scala Seq of class A * * @param jsonArray - * @tparam A + * @param A * @return * Seq[A] */ diff --git a/src/main/scala/com/campudus/tableaux/router/TableauxRouter.scala b/src/main/scala/com/campudus/tableaux/router/TableauxRouter.scala index 89df66e0..328189e8 100644 --- a/src/main/scala/com/campudus/tableaux/router/TableauxRouter.scala +++ b/src/main/scala/com/campudus/tableaux/router/TableauxRouter.scala @@ -522,9 +522,9 @@ class TableauxRouter(override val config: TableauxConfig, val controller: Tablea */ private def createRow(context: RoutingContext): Unit = { implicit val user = TableauxUser(context) + val json = getJson(context) def getOptionalValues = { - val json = getJson(context) if (json.containsKey("columns") && json.containsKey("rows")) { Some(toColumnValueSeq(json)) } else { @@ -543,9 +543,8 @@ class TableauxRouter(override val config: TableauxConfig, val controller: Tablea case _: NoJsonFoundException => None }) .get - - controller - .createRow(tableId, optionalValues) + val rowPermissionsOpt = getRowPermissionsOpt(json) + controller.createRow(tableId, optionalValues, rowPermissionsOpt) } ) } diff --git a/src/test/scala/com/campudus/tableaux/api/permission/CreateRowWithRowPermissionsTest.scala b/src/test/scala/com/campudus/tableaux/api/permission/CreateRowWithRowPermissionsTest.scala new file mode 100644 index 00000000..3ebca134 --- /dev/null +++ b/src/test/scala/com/campudus/tableaux/api/permission/CreateRowWithRowPermissionsTest.scala @@ -0,0 +1,74 @@ +package com.campudus.tableaux.api.permission + +import com.campudus.tableaux.database.DatabaseConnection +import com.campudus.tableaux.database.model.SystemModel +import com.campudus.tableaux.testtools.{RequestCreation, TableauxTestBase} +import com.campudus.tableaux.testtools.RequestCreation.{Identifier, TextCol} + +import io.vertx.ext.unit.TestContext +import io.vertx.ext.unit.junit.VertxUnitRunner +import io.vertx.scala.SQLConnection +import org.vertx.scala.core.json.Json + +import scala.concurrent.Future + +import org.junit.Assert._ +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(classOf[VertxUnitRunner]) +class CreateRowWithRowPermissionsTest extends TableauxTestBase { + + val createTableJson = Json.obj("name" -> "Test Nr. 1") + + def createTextColumnJson(name: String) = RequestCreation.Columns().add(RequestCreation.TextCol(name)).getJson + + def createNumberColumnJson(name: String) = RequestCreation.Columns().add(RequestCreation.NumericCol(name)).getJson + + @Test + def createMultipleFullRowsWithRowPermissions(implicit c: TestContext): Unit = { + okTest { + val sqlConnection = SQLConnection(this.vertxAccess(), databaseConfig) + val dbConnection = DatabaseConnection(this.vertxAccess(), sqlConnection) + + val valuesRow = Json.obj( + "columns" -> Json.arr(Json.obj("id" -> 1), Json.obj("id" -> 2)), + "rows" -> Json.arr( + Json.obj("values" -> Json.arr("Test Field 1", 2)), + Json.obj("values" -> Json.arr("Test Field 2", 5)) + ), + "rowPermissions" -> Json.arr( + "perm_group_1", + "perm_group_2" + ) + ) + val expectedJson = Json.obj( + "status" -> "ok", + "rows" -> Json.arr( + Json.obj("id" -> 1, "values" -> Json.arr("Test Field 1", 2)), + Json.obj("id" -> 2, "values" -> Json.arr("Test Field 2", 5)) + ) + ) + + for { + _ <- sendRequest("POST", "/tables", createTableJson) + _ <- sendRequest("POST", "/tables/1/columns", createTextColumnJson("Test Column 1")) + _ <- sendRequest("POST", "/tables/1/columns", createNumberColumnJson("Test Column 2")) + test <- sendRequest("POST", "/tables/1/rows", valuesRow) + + rowPermissions <- + dbConnection.query("SELECT row_permissions FROM user_table_1") + .map(_.getJsonArray("results").getJsonArray(0)) + + // rowPermissionsHistory <- + // dbConnection.query( + // "SELECT value FROM user_table_history_1 WHERE event = 'row_permission_changed' AND history_type = 'permission'" + // ) + // .map(_.getJsonArray("results").getJsonArray(0)) + } yield { + assertJSONEquals(expectedJson, test) + println(s"rowPermissions: $rowPermissions") + } + } + } +}