diff --git a/.gitignore b/.gitignore
index 51c1bda4f..b95e3780d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,7 +21,7 @@ Thumbs.db
# Intellij #
######################
.idea
-!.idea/runConfigurations
+!.idea/runConfigurations/
*.iml
**/build/
**/out/
diff --git a/.idea/runConfigurations/Build.xml b/.idea/runConfigurations/Build.xml
new file mode 100644
index 000000000..bf87367c8
--- /dev/null
+++ b/.idea/runConfigurations/Build.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Debug.xml b/.idea/runConfigurations/Debug.xml
new file mode 100644
index 000000000..cd0c090fa
--- /dev/null
+++ b/.idea/runConfigurations/Debug.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Open_Cypress.xml b/.idea/runConfigurations/Open_Cypress.xml
new file mode 100644
index 000000000..335acf359
--- /dev/null
+++ b/.idea/runConfigurations/Open_Cypress.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Run_Tests.xml b/.idea/runConfigurations/Run_Tests.xml
new file mode 100644
index 000000000..ce4cd7477
--- /dev/null
+++ b/.idea/runConfigurations/Run_Tests.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index f6b90f2dc..4451a9a0c 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -13,6 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+import org.apache.tools.ant.taskdefs.condition.Os
+
plugins {
id("java-library")
id("com.bmuschko.docker-remote-api")
@@ -50,8 +53,19 @@ allprojects {
}
}
-tasks.named("build") {
- dependsOn(":cogboard-app:test", ":cogboard-webapp:buildImage")
+tasks {
+ named("build") {
+ dependsOn(":cogboard-app:test", ":cogboard-webapp:buildImage")
+ }
+ register("cypressInit", Exec::class) {
+ setWorkingDir("./functional/cypress-tests")
+ commandLine("npm${getCMD()}", "install")
+ }
+ register("cypressOpen", Exec::class) {
+ setWorkingDir("./functional/cypress-tests")
+ commandLine("npx${getCMD()}", "cypress", "open")
+ dependsOn("cypressInit")
+ }
}
detekt {
@@ -68,4 +82,6 @@ apply(from = "gradle/prepareCogboardCompose.gradle.kts")
// Uncomment lines below so you can print tasks execution order easily
//gradle.taskGraph.whenReady {
// this.allTasks.forEach { logger.error(it.path + " " + it.name) }
-//}
\ No newline at end of file
+//}
+
+fun getCMD() = if (Os.isFamily(Os.FAMILY_WINDOWS)) ".cmd" else ""
\ No newline at end of file
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/BoardsController.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/BoardsController.kt
index 7ac89f0f6..cbcbb87c7 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/BoardsController.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/BoardsController.kt
@@ -1,40 +1,44 @@
package com.cognifide.cogboard.config.controller
import com.cognifide.cogboard.CogboardConstants
-import com.cognifide.cogboard.config.helper.EntityCleanupHelper
import com.cognifide.cogboard.config.service.BoardsConfigService
-import com.cognifide.cogboard.storage.ContentRepository
import io.vertx.core.AbstractVerticle
import io.vertx.core.json.JsonObject
class BoardsController(private val factory: ControllerFactory = ControllerFactory()) : AbstractVerticle() {
private lateinit var boardsConfigService: BoardsConfigService
- private lateinit var sender: ConfirmationSender
override fun start() {
- val contentRepository = ContentRepository()
- sender = ConfirmationSender(vertx)
- boardsConfigService = BoardsConfigService(
- contentRepository,
- EntityCleanupHelper(vertx)
- )
-
+ boardsConfigService = BoardsConfigService(vertx)
factory.create(CogboardConstants.Event.BOARDS_CONFIG, vertx, prepareConfig())
}
private fun prepareConfig() = mapOf String>(
+ "save" to { body -> save(body) },
"update" to { body -> update(body) },
+ "delete" to { body -> delete(body) },
"get" to { _ -> get() }
)
- private fun update(body: JsonObject): String {
+ private fun save(body: JsonObject): String {
boardsConfigService.saveBoardsConfig(body)
- sender.sendOk()
- return JsonObject()
- .put("message", "OK")
- .toString()
+ return OK_MESSAGE
+ }
+
+ private fun update(body: JsonObject): String {
+ boardsConfigService.updateBoard(body)
+ return OK_MESSAGE
+ }
+
+ private fun delete(body: JsonObject): String {
+ boardsConfigService.deleteBoard(body.getString("id"))
+ return OK_MESSAGE
}
private fun get() = boardsConfigService.loadBoardsConfig().toString()
+
+ companion object {
+ const val OK_MESSAGE = "{\"message\":\"OK\"}"
+ }
}
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/ConfirmationSender.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/ConfirmationSender.kt
deleted file mode 100644
index 5b1e013f8..000000000
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/ConfirmationSender.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.cognifide.cogboard.config.controller
-
-import com.cognifide.cogboard.CogboardConstants.Event
-import com.cognifide.cogboard.CogboardConstants.EventType
-import com.cognifide.cogboard.CogboardConstants.Props
-import io.vertx.core.Vertx
-import io.vertx.core.json.JsonObject
-
-class ConfirmationSender(private val vertx: Vertx) {
-
- fun sendOk() {
- vertx.eventBus().send(Event.SEND_MESSAGE_TO_WEBSOCKET, JsonObject().message(OK_MESSAGE))
- }
-
- private fun JsonObject.message(message: String): JsonObject {
- return this
- .put(Props.EVENT_TYPE, EventType.CONFIG_SAVED)
- .put("message", message)
- }
-
- companion object {
- private const val OK_MESSAGE = "Configuration saved"
- }
-}
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/WidgetsController.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/WidgetsController.kt
index f66ab489c..3c3f7e734 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/WidgetsController.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/controller/WidgetsController.kt
@@ -2,10 +2,8 @@ package com.cognifide.cogboard.config.controller
import com.cognifide.cogboard.CogboardConstants.Event
import com.cognifide.cogboard.CogboardConstants.Props
-import com.cognifide.cogboard.config.helper.EntityCleanupHelper
import com.cognifide.cogboard.config.service.BoardsConfigService
import com.cognifide.cogboard.config.service.WidgetRuntimeService
-import com.cognifide.cogboard.storage.ContentRepository
import io.vertx.core.AbstractVerticle
import io.vertx.core.json.JsonArray
import io.vertx.core.json.JsonObject
@@ -14,16 +12,12 @@ class WidgetsController : AbstractVerticle() {
private lateinit var boardsConfigService: BoardsConfigService
private lateinit var widgetRuntimeService: WidgetRuntimeService
+
override fun start() {
- val contentRepository = ContentRepository()
- boardsConfigService = BoardsConfigService(
- contentRepository,
- EntityCleanupHelper(vertx))
+ boardsConfigService = BoardsConfigService(vertx)
val allWidgets = boardsConfigService.getAllWidgets()
- widgetRuntimeService =
- WidgetRuntimeService(vertx, contentRepository)
- .init(allWidgets)
+ widgetRuntimeService = WidgetRuntimeService(vertx).init(allWidgets)
listenOnWidgetUpdate()
listenOnWidgetContentUpdate()
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/handler/HandlerUtil.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/handler/HandlerUtil.kt
index 4b48f6268..f007d8c91 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/handler/HandlerUtil.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/handler/HandlerUtil.kt
@@ -14,6 +14,6 @@ class HandlerUtil {
.end(body)
}
- val SESSION_REFRESHERS = setOf(HttpMethod.POST, HttpMethod.DELETE)
+ private val SESSION_REFRESHERS = setOf(HttpMethod.POST, HttpMethod.DELETE)
}
}
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Board.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Board.kt
index 311ca2349..1a5ab3c03 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Board.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Board.kt
@@ -11,7 +11,6 @@ data class Board(
val autoSwitch: Boolean = false,
val switchInterval: Int,
val widgets: List,
- val theme: String,
val type: String = "WidgetBoard",
@JsonIgnore
val dynamicFields: MutableMap = mutableMapOf()
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Widgets.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Widgets.kt
index 5c9af8eff..2e4074a0f 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Widgets.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/model/Widgets.kt
@@ -1,6 +1,5 @@
package com.cognifide.cogboard.config.model
data class Widgets(
- val widgetsById: Map,
- val allWidgets: List
+ val widgetsById: Map
)
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigHelper.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigHelper.kt
new file mode 100644
index 000000000..d49714af2
--- /dev/null
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigHelper.kt
@@ -0,0 +1,34 @@
+package com.cognifide.cogboard.config.service
+
+import com.cognifide.cogboard.CogboardConstants.Props
+import io.vertx.core.json.JsonArray
+import io.vertx.core.json.JsonObject
+
+class BoardsConfigHelper {
+ companion object {
+ fun removeBoard(config: JsonObject, id: String): JsonObject {
+ val copy = config.copy()
+ copy.allBoards()?.remove(id)
+ copy.boardsById()?.remove(id)
+ return copy
+ }
+
+ fun updateBoard(config: JsonObject, board: JsonObject): JsonObject {
+ val id = board.getString(Props.ID)
+ val copy = config.copy()
+ val prevWidgets = copy.boardsById()?.getJsonObject(id)?.getJsonArray(Props.WIDGETS) ?: JsonArray()
+ if (!copy.allBoards()?.contains(id)!!) {
+ copy.allBoards()?.add(id)
+ }
+ if (board.widgetsNotPresent()) {
+ board.put(Props.WIDGETS, prevWidgets)
+ }
+ copy.boardsById()?.put(id, board)
+ return copy
+ }
+ }
+}
+
+private fun JsonObject.allBoards(): JsonArray? = this.getJsonObject(Props.BOARDS).getJsonArray(Props.BOARDS_ALL)
+private fun JsonObject.boardsById(): JsonObject? = this.getJsonObject(Props.BOARDS).getJsonObject(Props.BOARDS_BY_ID)
+private fun JsonObject.widgetsNotPresent() = !this.containsKey(Props.WIDGETS)
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigService.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigService.kt
index ce6f1c7e4..7f81ff6da 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigService.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/BoardsConfigService.kt
@@ -1,23 +1,28 @@
package com.cognifide.cogboard.config.service
+import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.CogboardConstants.Props
import com.cognifide.cogboard.config.helper.EntityCleanupHelper
import com.cognifide.cogboard.storage.ContentRepository
import com.cognifide.cogboard.storage.Storage
import com.cognifide.cogboard.storage.VolumeStorageFactory.boards
import com.cognifide.cogboard.widget.WidgetIndex
+import io.vertx.core.Vertx
import io.vertx.core.json.JsonObject
class BoardsConfigService(
- private val contentRepository: ContentRepository = ContentRepository(),
- private val entityCleanupHelper: EntityCleanupHelper? = null,
+ private val vertx: Vertx? = null,
+ private val contentRepository: ContentRepository = ContentRepository.DEFAULT,
private val storage: Storage = boards()
) {
+ private var entityCleanupHelper: EntityCleanupHelper? = if (vertx == null) null else EntityCleanupHelper(vertx)
+
fun saveBoardsConfig(boardsConfig: JsonObject) {
val cleanBoardsConfig = executeForWidgets(boardsConfig, this::resetContentNode)
handleDeletedEntities(cleanBoardsConfig)
storage.saveConfig(cleanBoardsConfig)
+ sendOk()
}
fun loadBoardsConfig(): JsonObject {
@@ -33,13 +38,25 @@ class BoardsConfigService(
contentRepository.save(widgetId, content)
}
+ fun updateBoard(board: JsonObject) {
+ val config = storage.loadConfig()
+ val updatedConfig = BoardsConfigHelper.updateBoard(config, board)
+ saveBoardsConfig(updatedConfig)
+ }
+
+ fun deleteBoard(id: String) {
+ val config = storage.loadConfig()
+ val updatedConfig = BoardsConfigHelper.removeBoard(config, id)
+ saveBoardsConfig(updatedConfig)
+ }
+
fun getContent(widgetId: String): JsonObject {
return contentRepository.get(widgetId)
}
private fun getWidgetById(boardsConfig: JsonObject) =
- boardsConfig.getJsonObject(Props.WIDGETS)
- ?.getJsonObject(Props.WIDGETS_BY_ID) ?: JsonObject()
+ boardsConfig.getJsonObject(Props.WIDGETS)
+ ?.getJsonObject(Props.WIDGETS_BY_ID) ?: JsonObject()
private fun resetContentNode(widgetId: String, widget: JsonObject) {
widget.put(Props.CONTENT, JsonObject())
@@ -55,8 +72,8 @@ class BoardsConfigService(
): JsonObject {
val widgetsById = getWidgetById(boardsConfig)
widgetsById.fieldNames()
- .map { it to widgetsById.getJsonObject(it) }
- .forEach { action(it.first, it.second) }
+ .map { it to widgetsById.getJsonObject(it) }
+ .forEach { action(it.first, it.second) }
return boardsConfig
}
@@ -71,4 +88,14 @@ class BoardsConfigService(
.remove(it)
}
}
+
+ private fun sendOk() {
+ vertx?.eventBus()?.send(CogboardConstants.Event.SEND_MESSAGE_TO_WEBSOCKET, JsonObject().configSaved())
+ }
+
+ private fun JsonObject.configSaved(): JsonObject {
+ return this
+ .put(Props.EVENT_TYPE, CogboardConstants.EventType.CONFIG_SAVED)
+ .put("message", "Configuration saved")
+ }
}
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/WidgetRuntimeService.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/WidgetRuntimeService.kt
index 167122cc6..46abcd931 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/WidgetRuntimeService.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/config/service/WidgetRuntimeService.kt
@@ -13,10 +13,9 @@ import io.vertx.core.logging.Logger
import io.vertx.core.logging.LoggerFactory
import java.util.stream.Collectors
-class WidgetRuntimeService(
- private val vertx: Vertx,
- private val contentRepository: ContentRepository
-) {
+class WidgetRuntimeService(private val vertx: Vertx) {
+
+ private val contentRepository: ContentRepository = ContentRepository.DEFAULT
private val widgets = mutableMapOf()
fun init(widgetsById: JsonObject): WidgetRuntimeService {
diff --git a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/storage/ContentRepository.kt b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/storage/ContentRepository.kt
index 284719677..1f66ca8b5 100644
--- a/cogboard-app/src/main/kotlin/com/cognifide/cogboard/storage/ContentRepository.kt
+++ b/cogboard-app/src/main/kotlin/com/cognifide/cogboard/storage/ContentRepository.kt
@@ -20,4 +20,8 @@ class ContentRepository(private val configRoot: String = "/data") {
}
private fun getFile(widgetId: String) = File("$configRoot/content/$widgetId.json")
+
+ companion object {
+ val DEFAULT = ContentRepository()
+ }
}
diff --git a/cogboard-app/src/main/resources/cypressData/config.json b/cogboard-app/src/main/resources/cypressData/config.json
index 5a076fb86..af09cfa5a 100644
--- a/cogboard-app/src/main/resources/cypressData/config.json
+++ b/cogboard-app/src/main/resources/cypressData/config.json
@@ -43,7 +43,6 @@
"isUpdating": false,
"boardId": "board1"
}
- },
- "allWidgets": ["widget1", "widget2"]
+ }
}
}
diff --git a/cogboard-app/src/main/resources/initData/config.json b/cogboard-app/src/main/resources/initData/config.json
index 8c3d65e31..c4949e02f 100644
--- a/cogboard-app/src/main/resources/initData/config.json
+++ b/cogboard-app/src/main/resources/initData/config.json
@@ -155,86 +155,6 @@
]
},
"widgets": {
- "allWidgets": [
- "widget9",
- "widget10",
- "widget11",
- "widget12",
- "widget13",
- "widget14",
- "widget15",
- "widget16",
- "widget17",
- "widget18",
- "widget19",
- "widget20",
- "widget21",
- "widget22",
- "widget23",
- "widget24",
- "widget25",
- "widget26",
- "widget27",
- "widget29",
- "widget31",
- "widget33",
- "widget35",
- "widget36",
- "widget38",
- "widget40",
- "widget41",
- "widget42",
- "widget43",
- "widget44",
- "widget45",
- "widget46",
- "widget47",
- "widget48",
- "widget49",
- "widget50",
- "widget51",
- "widget52",
- "widget53",
- "widget54",
- "widget55",
- "widget56",
- "widget58",
- "widget59",
- "widget61",
- "widget62",
- "widget63",
- "widget64",
- "widget65",
- "widget66",
- "widget67",
- "widget68",
- "widget69",
- "widget70",
- "widget71",
- "widget72",
- "widget73",
- "widget74",
- "widget75",
- "widget76",
- "widget77",
- "widget78",
- "widget79",
- "widget80",
- "widget81",
- "widget82",
- "widget83",
- "widget84",
- "widget85",
- "widget86",
- "widget87",
- "widget88",
- "widget89",
- "widget90",
- "widget91",
- "widget92",
- "widget93",
- "widget94"
- ],
"widgetsById": {
"widget9": {
"id": "widget9",
diff --git a/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigHelperTest.kt b/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigHelperTest.kt
new file mode 100644
index 000000000..f03a4a5a2
--- /dev/null
+++ b/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigHelperTest.kt
@@ -0,0 +1,106 @@
+package com.cognifide.cogboard.config.service
+
+import com.cognifide.cogboard.TestHelper.Companion.readConfigFromResource
+import io.vertx.core.json.JsonObject
+import org.junit.jupiter.api.Assertions.assertTrue
+import org.junit.jupiter.api.Assertions.assertFalse
+import org.junit.jupiter.api.Assertions.assertNull
+import org.junit.jupiter.api.Assertions.assertNotNull
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.BeforeEach
+import org.junit.jupiter.api.Test
+
+class BoardsConfigHelperTest {
+
+ private lateinit var config: JsonObject
+ private lateinit var board3: JsonObject
+
+ @BeforeEach
+ fun beforeEach() {
+ config = readConfigFromResource("/com/cognifide/cogboard/config/config-with-2-boards.json")
+ board3 = readConfigFromResource("/com/cognifide/cogboard/config/board-3.json")
+ // initial check
+ assertEquals(2, config.getJsonObject("boards").getJsonArray("allBoards").size(), "initial size")
+ assertTrue(config.getJsonObject("boards").getJsonArray("allBoards").contains("board-1"))
+ assertTrue(config.getJsonObject("boards").getJsonArray("allBoards").contains("board-2"))
+ assertNotNull(config.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-1"))
+ assertNotNull(config.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-2"))
+ }
+
+ @Test
+ fun `Expect board will be deleted from config`() {
+ val updatedConfig = BoardsConfigHelper.removeBoard(config, "board-2")
+
+ assertEquals(
+ 1,
+ updatedConfig.getJsonObject("boards").getJsonArray("allBoards").size(),
+ "allBoards size"
+ )
+ assertFalse(
+ updatedConfig.getJsonObject("boards").getJsonArray("allBoards").contains("board-2"),
+ "allBoards should not contain board-2 string"
+ )
+ assertNull(
+ updatedConfig.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-2"),
+ "boardsById should not contain board-2 object"
+ )
+ }
+
+ @Test
+ fun `Expect board will be added to config`() {
+ val updatedConfig = BoardsConfigHelper.updateBoard(config, board3)
+
+ assertEquals(
+ 3,
+ updatedConfig.getJsonObject("boards").getJsonArray("allBoards").size(),
+ "allBoards size"
+ )
+ assertTrue(
+ updatedConfig.getJsonObject("boards").getJsonArray("allBoards").contains("board-3"),
+ "allBoards should contain board-3 string"
+ )
+ assertNotNull(
+ updatedConfig.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-3"),
+ "boardsById should contain board-3 object"
+ )
+ }
+
+ @Test
+ fun `Expect board will be updated`() {
+ board3.put("id", "board-2")
+ val updatedConfig = BoardsConfigHelper.updateBoard(config, board3)
+
+ assertEquals(
+ 2,
+ updatedConfig.getJsonObject("boards").getJsonArray("allBoards").size(),
+ "allBoards size"
+ )
+ assertTrue(
+ updatedConfig.getJsonObject("boards").getJsonArray("allBoards").contains("board-2"),
+ "allBoards should contain board-2 string"
+ )
+ assertNotNull(
+ updatedConfig.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-2"),
+ "boardsById should contain board-2 object"
+ )
+ assertEquals(
+ "TestBoard 3",
+ updatedConfig.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-2")
+ .getString("title")
+ )
+ }
+
+ @Test
+ fun `Expect widgets will not be deleted after board props updated`() {
+ board3.put("id", "board-2")
+ val updatedConfig = BoardsConfigHelper.updateBoard(config, board3)
+
+ assertEquals(
+ 1,
+ updatedConfig.getJsonObject("boards").getJsonObject("boardsById").getJsonObject("board-2")
+ .getJsonArray("widgets").size(),
+ "board-2 still contains widgets"
+ )
+ }
+}
+
diff --git a/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigServiceTest.kt b/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigServiceTest.kt
index 8836dc4bc..7dcba6715 100644
--- a/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigServiceTest.kt
+++ b/cogboard-app/src/test/kotlin/com/cognifide/cogboard/config/service/BoardsConfigServiceTest.kt
@@ -13,7 +13,7 @@ internal class BoardsConfigServiceTest {
private val boardPath = BoardsConfigServiceTest::class.java.getResource("/board").path
private fun initService(configFilePath: String = "$boardPath/config-on-backend.json"): BoardsConfigService {
- return BoardsConfigService(ContentRepository(boardPath), storage = boards(configFilePath))
+ return BoardsConfigService(contentRepository = ContentRepository(boardPath), storage = boards(configFilePath))
}
@Test
diff --git a/cogboard-app/src/test/kotlin/com/cognifide/cogboard/widget/type/WidgetTestBase.kt b/cogboard-app/src/test/kotlin/com/cognifide/cogboard/widget/type/WidgetTestBase.kt
index 1cbed7d04..3ff51757a 100644
--- a/cogboard-app/src/test/kotlin/com/cognifide/cogboard/widget/type/WidgetTestBase.kt
+++ b/cogboard-app/src/test/kotlin/com/cognifide/cogboard/widget/type/WidgetTestBase.kt
@@ -46,7 +46,7 @@ abstract class WidgetTestBase {
fun initService(): BoardsConfigService {
val boardPath = JenkinsJobWidgetTest::class.java.getResource("/board").path
val contentRepository = ContentRepository(boardPath)
- return BoardsConfigService(contentRepository)
+ return BoardsConfigService(contentRepository = contentRepository)
}
fun captureWhatIsSent(eventBus: EventBus, captor: ArgumentCaptor): Pair {
diff --git a/cogboard-app/src/test/resources/board/config-from-frontend.json b/cogboard-app/src/test/resources/board/config-from-frontend.json
index 8aefe42e5..0f838e942 100644
--- a/cogboard-app/src/test/resources/board/config-from-frontend.json
+++ b/cogboard-app/src/test/resources/board/config-from-frontend.json
@@ -48,7 +48,6 @@
"isUpdating": false,
"boardId": "board1"
}
- },
- "allWidgets": ["newWidget,widgetThatIsNotEdited"]
+ }
}
}
diff --git a/cogboard-app/src/test/resources/board/config-on-backend.json b/cogboard-app/src/test/resources/board/config-on-backend.json
index 3df4ece64..b254763ce 100644
--- a/cogboard-app/src/test/resources/board/config-on-backend.json
+++ b/cogboard-app/src/test/resources/board/config-on-backend.json
@@ -48,7 +48,6 @@
"isUpdating": false,
"boardId": "board1"
}
- },
- "allWidgets": ["widgetThatWillBeReplaced,widgetThatIsNotEdited"]
+ }
}
}
diff --git a/cogboard-app/src/test/resources/com/cognifide/cogboard/config/board-3.json b/cogboard-app/src/test/resources/com/cognifide/cogboard/config/board-3.json
new file mode 100644
index 000000000..4fd16d752
--- /dev/null
+++ b/cogboard-app/src/test/resources/com/cognifide/cogboard/config/board-3.json
@@ -0,0 +1,9 @@
+{
+ "autoSwitch": false,
+ "switchInterval": 360,
+ "id": "board-3",
+ "theme": "default",
+ "columns": 8,
+ "title": "TestBoard 3",
+ "type": "WidgetBoard"
+}
diff --git a/cogboard-app/src/test/resources/com/cognifide/cogboard/config/config-with-2-boards.json b/cogboard-app/src/test/resources/com/cognifide/cogboard/config/config-with-2-boards.json
new file mode 100644
index 000000000..68499fd25
--- /dev/null
+++ b/cogboard-app/src/test/resources/com/cognifide/cogboard/config/config-with-2-boards.json
@@ -0,0 +1,69 @@
+{
+ "boards": {
+ "boardsById": {
+ "board-1": {
+ "autoSwitch": false,
+ "switchInterval": 360,
+ "id": "board-1",
+ "theme": "default",
+ "widgets": ["widget1"],
+ "columns": 8,
+ "title": "TestBoard 1",
+ "type": "WidgetBoard"
+ },
+ "board-2": {
+ "autoSwitch": false,
+ "switchInterval": 360,
+ "id": "board-2",
+ "theme": "default",
+ "widgets": ["widget2"],
+ "columns": 8,
+ "title": "TestBoard 2",
+ "type": "WidgetBoard"
+ }
+ },
+ "allBoards": ["board-1", "board-2"]
+ },
+ "widgets": {
+ "widgetsById": {
+ "widget1": {
+ "id": "widget1",
+ "title": "",
+ "config": {
+ "columns": 4,
+ "goNewLine": false,
+ "rows": 0.5
+ },
+ "type": "TextWidget",
+ "disabled": false,
+ "content": {},
+ "status": "UNKNOWN",
+ "isUpdating": false,
+ "boardId": "board-21b6c72f-a4ac-4c62-adc4-8a2ec8c5a71d",
+ "text": "CogBoard",
+ "textSize": "h4",
+ "isVertical": false,
+ "expandContent": false
+ },
+ "widget2": {
+ "id": "widget2",
+ "title": "",
+ "config": {
+ "columns": 4,
+ "goNewLine": false,
+ "rows": 0.5
+ },
+ "type": "TextWidget",
+ "disabled": false,
+ "content": {},
+ "status": "UNKNOWN",
+ "isUpdating": false,
+ "boardId": "board-21b6c72f-a4ac-4c62-adc4-8a2ec8c5a71d",
+ "text": "CogBoard",
+ "textSize": "h4",
+ "isVertical": false,
+ "expandContent": false
+ }
+ }
+ }
+}
diff --git a/cogboard-webapp/src/actions/actionCreators.js b/cogboard-webapp/src/actions/actionCreators.js
index eeb14828d..9d0cfcc88 100644
--- a/cogboard-webapp/src/actions/actionCreators.js
+++ b/cogboard-webapp/src/actions/actionCreators.js
@@ -79,14 +79,9 @@ export const updateWidget = data => ({
payload: data
});
-export const addBoard = data => ({
+export const addBoard = newBoard => ({
type: ADD_BOARD,
- payload: {
- id: `board-${v4()}`,
- theme: 'default',
- widgets: [],
- ...data
- }
+ payload: newBoard
});
export const deleteBoard = id => ({
diff --git a/cogboard-webapp/src/actions/helpers.js b/cogboard-webapp/src/actions/helpers.js
index 6a370c092..30117dc84 100644
--- a/cogboard-webapp/src/actions/helpers.js
+++ b/cogboard-webapp/src/actions/helpers.js
@@ -1,27 +1,23 @@
import { dataChanged, setLogoutReasonMessage } from './actionCreators';
import { logout } from './thunks';
import { isAuthenticated, setToken } from '../utils/auth';
-import { mergeRight, assocPath } from 'ramda';
+import { assocPath } from 'ramda';
import { checkResponseStatus } from '../utils/fetch';
export const fetchData = (
url,
{ method = 'GET', data = {}, token = '' } = {}
) => {
- const baseConfig = { method };
-
- const configMap = {
- GET: baseConfig,
- DELETE: baseConfig,
- POST: mergeRight(baseConfig, {
- body: JSON.stringify(data),
- headers: {
- 'Content-Type': 'application/json'
- }
- })
- };
-
- const config = method in configMap ? configMap[method] : configMap['POST'];
+ const config =
+ method === 'POST'
+ ? {
+ method,
+ body: JSON.stringify(data),
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ }
+ : { method };
const authenticationConfig = token
? assocPath(['headers', 'Authorization'], token, config)
@@ -69,11 +65,11 @@ const mapFormValuesToWidgetData = values => {
export const createNewWidgetData = ({
values,
- allWidgets,
+ allWidgetNames,
currentBoardId
}) => ({
boardId: currentBoardId,
- id: createWidgetId(allWidgets),
+ id: createWidgetId(allWidgetNames),
...mapFormValuesToWidgetData(values)
});
diff --git a/cogboard-webapp/src/actions/thunks.js b/cogboard-webapp/src/actions/thunks.js
index 7224d78d0..b43dfd3c6 100644
--- a/cogboard-webapp/src/actions/thunks.js
+++ b/cogboard-webapp/src/actions/thunks.js
@@ -49,6 +49,7 @@ import {
getGuestName
} from '../utils/auth';
import { newVersionButtonsCreator } from '../components/NewVersionButtons/helpers';
+import { v4 } from 'uuid';
export const fetchInitialData = () => dispatch => {
dispatch(requestData());
@@ -71,7 +72,6 @@ export const saveDataThunk = () => (dispatch, getState) => {
const state = getState();
const { boards } = state;
const widgets = {
- allWidgets: state.widgets.allWidgets,
widgetsById: state.widgets.widgetsById
};
const data = { boards, widgets };
@@ -83,6 +83,16 @@ export const saveDataThunk = () => (dispatch, getState) => {
);
};
+const boardApiCall = (board, action) => dispatch => {
+ const token = getToken();
+ const url = `${URL.BOARD}/${board.id}`;
+
+ return fetchData(url, { method: action, data: board, token }).then(
+ () => dispatch(saveDataSuccess()),
+ console.error
+ );
+};
+
export const login = (credentials, loginAsGuest) => dispatch => {
if (loginAsGuest) {
const guestName = credentials.username;
@@ -123,6 +133,7 @@ const deleteBoardWithWidgetsThunk = id => (dispatch, getState) => {
const { currentBoard } = ui;
dispatch(deleteBoard(id));
+ dispatch(boardApiCall({ id: id }, 'DELETE'));
const [firstBoardId] = getState().boards.allBoards;
@@ -134,13 +145,34 @@ const deleteBoardWithWidgetsThunk = id => (dispatch, getState) => {
dispatch(deleteMultipleWidgets(widgets));
};
+const addBoardThunk = data => dispatch => {
+ const newBoard = {
+ id: `board-${v4()}`,
+ theme: 'default',
+ widgets: [],
+ ...data
+ };
+
+ dispatch(addBoard(newBoard));
+ dispatch(boardApiCall(newBoard, 'POST'));
+};
+
+const editBoardThunk = data => dispatch => {
+ dispatch(editBoard(data));
+ dispatch(boardApiCall(data, 'POST'));
+};
+
const makeWidgetUpdaterThunk = (
beforeUpdateActionCreator,
widgetDataCreator
) => data => (dispatch, getState) => {
- const allWidgets = getState().widgets.allWidgets;
+ const allWidgetNames = [];
+ // eslint-disable-next-line array-callback-return
+ Object.entries(getState().widgets.widgetsById).map(item => {
+ allWidgetNames.push(item[0]);
+ });
const token = getToken();
- const widgetData = widgetDataCreator({ ...data, allWidgets });
+ const widgetData = widgetDataCreator({ ...data, allWidgetNames });
const { id } = widgetData;
const { generalData, serverData } = mapDataToState(widgetData);
@@ -322,8 +354,8 @@ export const removeWidget = withAuthentication(
);
export const reorderWidgets = withDataChanged(reorderWidgetsThunk);
export const reorderBoard = withDataChanged(reorderBoards);
-export const addNewBoard = withDataChanged(addBoard);
-export const saveBoard = withDataChanged(editBoard);
+export const addNewBoard = withDataChanged(addBoardThunk);
+export const saveBoard = withDataChanged(editBoardThunk);
export const deleteBoardWithWidgets = withDataChanged(
deleteBoardWithWidgetsThunk
);
diff --git a/cogboard-webapp/src/constants/index.js b/cogboard-webapp/src/constants/index.js
index 277842657..fad3504a4 100644
--- a/cogboard-webapp/src/constants/index.js
+++ b/cogboard-webapp/src/constants/index.js
@@ -21,6 +21,7 @@ export const URL = {
LOAD_INFO: '/api/version',
LOAD_DATA: '/api/config',
SAVE_DATA: '/api/config/save',
+ BOARD: '/api/board',
UPDATE_WIDGET: '/api/widget/update',
DELETE_WIDGET: '/api/widget/delete',
LOGIN: '/api/login',
diff --git a/cogboard-webapp/src/reducers/widgets/allWidgets.js b/cogboard-webapp/src/reducers/widgets/allWidgets.js
deleted file mode 100644
index 3950a4093..000000000
--- a/cogboard-webapp/src/reducers/widgets/allWidgets.js
+++ /dev/null
@@ -1,51 +0,0 @@
-import {
- RECEIVE_DATA,
- ADD_WIDGET,
- DELETE_WIDGET,
- DELETE_MULTIPLE_WIDGETS
-} from '../../actions/types';
-
-const receiveData = (state, { payload }) => {
- const {
- widgets: { allWidgets }
- } = payload;
-
- return [...state, ...allWidgets];
-};
-
-const addWidget = (state, { payload }) => {
- const { id } = payload;
-
- return [...state, id];
-};
-
-const deleteWidget = (state, { payload }) => {
- const { id } = payload;
-
- return state.filter(widgetId => widgetId !== id);
-};
-
-const deleteMultipleWidgets = (state, { payload }) => {
- const { widgetIds } = payload;
-
- return state.filter(widgetId => !widgetIds.includes(widgetId));
-};
-
-const allWidgets = (state = [], action) => {
- const { type } = action;
-
- switch (type) {
- case RECEIVE_DATA:
- return receiveData(state, action);
- case ADD_WIDGET:
- return addWidget(state, action);
- case DELETE_WIDGET:
- return deleteWidget(state, action);
- case DELETE_MULTIPLE_WIDGETS:
- return deleteMultipleWidgets(state, action);
- default:
- return state;
- }
-};
-
-export default allWidgets;
diff --git a/cogboard-webapp/src/reducers/widgets/index.js b/cogboard-webapp/src/reducers/widgets/index.js
index 32a5b14b7..06d145c87 100644
--- a/cogboard-webapp/src/reducers/widgets/index.js
+++ b/cogboard-webapp/src/reducers/widgets/index.js
@@ -1,12 +1,10 @@
import { combineReducers } from 'redux';
import widgetsById from './widgetsById';
-import allWidgets from './allWidgets';
import widgetTypes from './widgetTypes';
const widgets = combineReducers({
widgetsById,
- allWidgets,
widgetTypes
});
diff --git a/functional/cypress-tests/cypress/fixtures/Dashboard.js b/functional/cypress-tests/cypress/fixtures/Dashboard.js
index c02c0d636..274f53187 100644
--- a/functional/cypress-tests/cypress/fixtures/Dashboard.js
+++ b/functional/cypress-tests/cypress/fixtures/Dashboard.js
@@ -6,11 +6,6 @@ export const columnEdgeValues = ['3', '4', '20', '21'];
export const switchIntervalEdgeValues = ['2', '3'];
-export const dashboardNames = [
- dashboardNameGen(),
- 'Long Dashboard Name Input. It has exactly 51 chars.'
-];
-
export const dashboardTypes = {
widgets: 'Widget Board',
iframe: 'Iframe Board'
diff --git a/functional/cypress-tests/cypress/fixtures/reorderingConfig.json b/functional/cypress-tests/cypress/fixtures/reorderingConfig.json
index 2e667f7ed..b90c52582 100644
--- a/functional/cypress-tests/cypress/fixtures/reorderingConfig.json
+++ b/functional/cypress-tests/cypress/fixtures/reorderingConfig.json
@@ -78,7 +78,6 @@
"isUpdating": false,
"schedulePeriod": 120
}
- },
- "allWidgets": ["widget_1", "widget_2", "widget_3"]
+ }
}
}
diff --git a/functional/cypress-tests/cypress/integration/dashboards.js b/functional/cypress-tests/cypress/integration/dashboards.js
index da27e76f2..825308645 100644
--- a/functional/cypress-tests/cypress/integration/dashboards.js
+++ b/functional/cypress-tests/cypress/integration/dashboards.js
@@ -1,10 +1,15 @@
import {
dashboardNameGen,
columnEdgeValues,
- switchIntervalEdgeValues,
- dashboardNames
+ switchIntervalEdgeValues
} from '../fixtures/Dashboard';
-import { addIframeDashboard, addWidgetsDashboard } from '../support/dashboard';
+import {
+ addIframeDashboard,
+ addWidgetsDashboard,
+ deleteDashboard
+} from '../support/dashboard';
+
+let dashboardName;
describe('Basic Dashboard CRUD', () => {
const newTitle = dashboardNameGen('Edit');
@@ -12,46 +17,39 @@ describe('Basic Dashboard CRUD', () => {
beforeEach(() => {
cy.visit('/');
cy.login();
+ dashboardName = dashboardNameGen();
});
- it('Logged user can add new widgets dashboard', () => {
- addWidgetsDashboard();
- });
-
- it('Logged user can choose widgets dashboard', () => {
- addWidgetsDashboard().canBeSelected();
+ afterEach(() => {
+ deleteDashboard(dashboardName);
});
- it('Logged user can add new iframe dashboard', () => {
- addIframeDashboard();
+ it('Logged user can add Dashboard for Widgets', () => {
+ addWidgetsDashboard(dashboardName).canBeSelected();
});
- it('Logged user can choose iframe dashboard', () => {
- addIframeDashboard()
+ it('Logged user can add Dashboard for iFrame', () => {
+ addIframeDashboard(dashboardName)
.canBeSelected()
.assertIframeExists();
});
it('Anonymous user can choose dashboard', () => {
- const board = addWidgetsDashboard();
+ const board = addWidgetsDashboard(dashboardName);
cy.closeDrawer();
cy.logout();
cy.get('[data-cy="navbar-show-drawer-button"]').click();
board.canBeSelected();
+ cy.login();
});
it('Logged user can edit dashboard', () => {
- addWidgetsDashboard()
+ addWidgetsDashboard(dashboardName)
.openEditDialog()
.setTitle(newTitle)
.confirmChanges()
.assertCardTitle(newTitle);
- });
-
- it('Logged user can remove dashboard', () => {
- addWidgetsDashboard()
- .delete()
- .assertNotVisible();
+ dashboardName = newTitle;
});
});
@@ -59,32 +57,27 @@ describe('Dashboard Input Validation', () => {
beforeEach(() => {
cy.visit('/');
cy.login();
+ dashboardName = dashboardNameGen();
});
- it('Name input do not accept empty strings', () => {
+ it('Board title cannot be empty strings', () => {
addWidgetsDashboard(' ')
.expectConfigToBeInvalid()
.assertErrorMessageVisible('This field is required');
});
- dashboardNames.forEach(value => {
- it(`Name input accepts strings not exceeding 50 characters. Tested value: ${value.length}`, () => {
- const board = addWidgetsDashboard(value);
- if (value.length > 50) {
- board
- .expectConfigToBeInvalid()
- .assertErrorMessageVisible(
- 'Title length must be less or equal to 50.'
- );
- } else {
- board.expectConfigToBeValid();
- }
- });
+ it('Board title cannot be more than 50 chars', () => {
+ dashboardName = 'Long Dashboard Name Input. It has exactly 51 chars.';
+ addWidgetsDashboard(dashboardName)
+ .expectConfigToBeInvalid()
+ .assertErrorMessageVisible('Title length must be less or equal to 50.');
});
columnEdgeValues.forEach(value => {
- it(`Columns input accepts only values from 4 to 20. Tested value: ${value}`, () => {
- const board = addWidgetsDashboard(undefined, value);
+ it(`Board Columns ${value} is ${
+ value < 4 || value > 20 ? 'IN' : ''
+ }VALID`, () => {
+ const board = addWidgetsDashboard(dashboardName, value);
if (value < 4) {
board
.expectConfigToBeInvalid()
@@ -95,13 +88,16 @@ describe('Dashboard Input Validation', () => {
.assertErrorMessageVisible('Columns number cannot be more than 20.');
} else {
board.expectConfigToBeValid();
+ deleteDashboard(dashboardName);
}
});
});
switchIntervalEdgeValues.forEach(value => {
- it(`Switch interval input accepts only values grater than 2. Tested value: ${value}`, () => {
- const board = addWidgetsDashboard(undefined, undefined, value);
+ it(`Board Switch Interval ${value} is ${
+ value < 3 ? 'IN' : ''
+ }VALID`, () => {
+ const board = addWidgetsDashboard(dashboardName, undefined, value);
if (value < 3) {
board
.expectConfigToBeInvalid()
@@ -110,6 +106,7 @@ describe('Dashboard Input Validation', () => {
);
} else {
board.expectConfigToBeValid();
+ deleteDashboard(dashboardName);
}
});
});
@@ -119,55 +116,57 @@ describe('Dashboard switcher', () => {
beforeEach(() => {
cy.visit('/');
cy.login();
+ dashboardName = dashboardNameGen();
});
it('Manual switching works', () => {
- let prevDashboardName, nextDashboardName;
-
- addWidgetsDashboard()
- .select()
- .assertTitle();
-
- prevDashboardName = cy
- .get('[data-cy="previous-board-button"]')
- .invoke('attr', 'title')
- .then(title => {
- prevDashboardName = title.toString();
- cy.get('[data-cy="previous-board-button"]').click();
- cy.contains(
- '[data-cy="navbar-title-header"]',
- `${prevDashboardName}`
- ).should('is.visible');
- })
- .toString();
- cy.get('[data-cy="next-board-button"]')
- .invoke('attr', 'title')
- .then(title => {
- nextDashboardName = title.toString();
- cy.get('[data-cy="next-board-button"]').click();
- cy.contains(
- '[data-cy="navbar-title-header"]',
- `${nextDashboardName}`
- ).should('is.visible');
- });
+ const prevDashboardName = 'PREV_' + dashboardName;
+ const middleDashboardName = 'MIDDLE_' + dashboardName;
+ const nextDashboardName = 'NEXT_' + dashboardName;
+
+ addWidgetsDashboard(prevDashboardName).select();
+ const middle = addWidgetsDashboard(middleDashboardName).select();
+ addWidgetsDashboard(nextDashboardName).select();
+
+ cy.get('[data-cy="navbar-show-drawer-button"]').click();
+ middle.select();
+
+ cy.get('[data-cy="previous-board-button"]').click();
+ cy.contains(
+ '[data-cy="navbar-title-header"]',
+ `${prevDashboardName}`
+ ).should('is.visible');
+
+ cy.get('[data-cy="navbar-show-drawer-button"]').click();
+ middle.select();
+
+ cy.get('[data-cy="next-board-button"]').click();
+ cy.contains(
+ '[data-cy="navbar-title-header"]',
+ `${nextDashboardName}`
+ ).should('is.visible');
+
+ deleteDashboard(prevDashboardName);
+ deleteDashboard(middleDashboardName);
+ deleteDashboard(nextDashboardName);
});
it('Automatic switching works', () => {
- let nextDashboardName;
-
- addWidgetsDashboard(undefined, undefined, '3')
- .select()
- .assertTitle();
-
- cy.get('[data-cy="next-board-button"]')
- .invoke('attr', 'title')
- .then(title => {
- nextDashboardName = title.toString();
- cy.get('[data-cy="auto-switch-board-button"]').click();
- cy.contains(
- '[data-cy="navbar-title-header"]',
- `${nextDashboardName}`
- ).should('is.visible');
- });
+ const nextDashboardName = 'NEXT_' + dashboardName;
+
+ const first = addWidgetsDashboard(dashboardName, undefined, '3').select();
+ addWidgetsDashboard(nextDashboardName).select();
+
+ cy.get('[data-cy="navbar-show-drawer-button"]').click();
+ first.select();
+
+ cy.get('[data-cy="auto-switch-board-button"]').click();
+ cy.contains(
+ '[data-cy="navbar-title-header"]',
+ `${nextDashboardName}`
+ ).should('is.visible');
+
+ deleteDashboard(dashboardName);
+ deleteDashboard(nextDashboardName);
});
});
diff --git a/functional/cypress-tests/cypress/integration/reordering_widgets.js b/functional/cypress-tests/cypress/integration/reordering_widgets.js
index a8581a7ee..8cccf82c1 100644
--- a/functional/cypress-tests/cypress/integration/reordering_widgets.js
+++ b/functional/cypress-tests/cypress/integration/reordering_widgets.js
@@ -1,21 +1,20 @@
import { dashboardNameGen } from '../fixtures/Dashboard';
import { createWidget } from '../support/widget';
import Widgets from '../fixtures/Widgets';
-import { addWidgetsDashboard } from '../support/dashboard';
+import { addWidgetsDashboard, deleteDashboard } from '../support/dashboard';
describe('Reordering widgets', () => {
const firstWidgetsTitle = 'First Widget';
const secondWidgetsTitle = 'Second Widget';
const thirdWidgetsTitle = 'Third Widget';
- let first, second, third, dashboard;
+ let first, second, third, dashboardName;
before(() => {
cy.visit('/');
cy.login();
- dashboard = addWidgetsDashboard(
- dashboardNameGen('ReorderWidgetsTest')
- ).select();
+ dashboardName = dashboardNameGen('ReorderWidgetsTest')
+ addWidgetsDashboard(dashboardName).select();
first = createCheckboxWidget(firstWidgetsTitle);
second = createCheckboxWidget(secondWidgetsTitle);
third = createCheckboxWidget(thirdWidgetsTitle);
@@ -54,7 +53,7 @@ describe('Reordering widgets', () => {
cy.login();
cy.get('[data-cy="navbar-show-drawer-button"]').click();
- dashboard.delete();
+ deleteDashboard(dashboardName)
});
function createCheckboxWidget(title) {
diff --git a/functional/cypress-tests/cypress/integration/saving.js b/functional/cypress-tests/cypress/integration/saving.js
index 4563233c4..3e4612557 100644
--- a/functional/cypress-tests/cypress/integration/saving.js
+++ b/functional/cypress-tests/cypress/integration/saving.js
@@ -10,18 +10,9 @@ describe('Dashboard Persistence', () => {
cy.login();
});
- it('Not saved dashboard is not displayed after refresh', () => {
- const name = dashboardNameGen();
- cy.addDashboard(name);
- cy.visit('/');
- cy.get('[data-cy="navbar-show-drawer-button"]').click();
- cy.contains('[data-cy="board-card"]', name).should('not.visible');
- });
-
it('Saved dashboard is displayed after refresh', () => {
const name = dashboardNameGen();
cy.addDashboard(name);
- cy.saveState();
cy.visit('/');
cy.get('[data-cy="navbar-show-drawer-button"]').click();
cy.contains('[data-cy="board-card"]', name)
@@ -32,7 +23,6 @@ describe('Dashboard Persistence', () => {
.scrollIntoView()
.click();
cy.get('[data-cy="confirmation-dialog-ok"]').click();
- cy.saveState();
});
});
diff --git a/functional/cypress-tests/cypress/support/dashboard.js b/functional/cypress-tests/cypress/support/dashboard.js
index d46f039bc..173c2740c 100644
--- a/functional/cypress-tests/cypress/support/dashboard.js
+++ b/functional/cypress-tests/cypress/support/dashboard.js
@@ -95,3 +95,15 @@ export function addWidgetsDashboard(name, columns, switchInterval) {
export function addIframeDashboard(name, columns, switchInterval) {
return new Dashboard(name, dashboardTypes.iframe, columns, switchInterval);
}
+
+export function deleteDashboard(name) {
+ cy.get('[data-cy="navbar-show-drawer-button"]').then(el => {
+ return el.click();
+ });
+ cy.contains('[data-cy="board-card"]', name)
+ .find('[data-cy="board-card-delete-button"]')
+ .scrollIntoView()
+ .click();
+ cy.get('[data-cy="confirmation-dialog-ok"]').click();
+ return this;
+}
diff --git a/functional/cypress-tests/cypress/support/helpers.js b/functional/cypress-tests/cypress/support/helpers.js
index cdf0e70c8..b463510ec 100644
--- a/functional/cypress-tests/cypress/support/helpers.js
+++ b/functional/cypress-tests/cypress/support/helpers.js
@@ -1,15 +1,11 @@
export const renewConfig = (config, newConfig) => {
const {
- boards: { allBoards },
- widgets: { allWidgets }
+ boards: { allBoards }
} = config;
const filteredBoards = allBoards.filter(
id => !newConfig.boards.allBoards.includes(id)
);
- const filteredWidgets = allWidgets.filter(
- id => !newConfig.widgets.allWidgets.includes(id)
- );
const result = {
...config,
@@ -26,8 +22,7 @@ export const renewConfig = (config, newConfig) => {
widgetsById: {
...config.widgets.widgetsById,
...newConfig.widgets.widgetsById
- },
- allWidgets: [...filteredWidgets, ...newConfig.widgets.allWidgets]
+ }
}
};
return result;
diff --git a/knotx/conf/openapi.yaml b/knotx/conf/openapi.yaml
index 4d386e24a..f04d96eb8 100644
--- a/knotx/conf/openapi.yaml
+++ b/knotx/conf/openapi.yaml
@@ -47,6 +47,28 @@ paths:
responses:
default:
description: Boards Save Handler
+ /api/board/{id}:
+ parameters:
+ - name: id
+ in: path
+ description: board add, update, delete
+ required: true
+ schema:
+ type: string
+ post:
+ operationId: boards-update-handler
+ security:
+ - cogboardAuth: []
+ responses:
+ default:
+ description: Board Add/Update Handler
+ delete:
+ operationId: boards-delete-handler
+ security:
+ - cogboardAuth: []
+ responses:
+ default:
+ description: Board Delete Handler
/api/login:
post:
operationId: login-handler
diff --git a/knotx/conf/routes/operations.conf b/knotx/conf/routes/operations.conf
index 4c1adc495..aff63a164 100644
--- a/knotx/conf/routes/operations.conf
+++ b/knotx/conf/routes/operations.conf
@@ -153,6 +153,20 @@ routingOperations = ${routingOperations} [
}
{
operationId = boards-save-handler
+ handlers = [
+ {
+ name = config-api-handler
+ config {
+ address = cogboard.config.boards
+ method = save
+ payload = body
+ refresh = false
+ }
+ }
+ ]
+ }
+ {
+ operationId = boards-update-handler
handlers = [
{
name = config-api-handler
@@ -165,6 +179,20 @@ routingOperations = ${routingOperations} [
}
]
}
+ {
+ operationId = boards-delete-handler
+ handlers = [
+ {
+ name = config-api-handler
+ config {
+ address = cogboard.config.boards
+ method = delete
+ payload = params
+ refresh = false
+ }
+ }
+ ]
+ }
{
operationId = boards-get-handler
handlers = [