Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Bug-fixes - widgets not refreshed correctly | Jenkins Job widget error handling added #78

Merged
merged 2 commits into from
Oct 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.cognifide.cogboard.config.controller

import com.cognifide.cogboard.CogboardConstants
import com.cognifide.cogboard.config.EndpointLoader
import com.cognifide.cogboard.storage.Storage
import com.cognifide.cogboard.storage.docker.Validation
Expand All @@ -11,39 +10,64 @@ import io.vertx.core.AbstractVerticle
import io.vertx.core.json.JsonObject
import io.vertx.core.logging.Logger
import io.vertx.core.logging.LoggerFactory
import com.cognifide.cogboard.CogboardConstants as CC
Copy link
Contributor Author

@szymon-owczarzak szymon-owczarzak Oct 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Local alias for class :) very useful for readability


internal open class BoardsController : AbstractVerticle() {
class BoardsAndWidgetsController : AbstractVerticle() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to merge BoardsController and WidgetsController back together because they have to share the same widgets map object.


private lateinit var storage: Storage
internal val widgets = mutableMapOf<String, Widget>()
private val widgets = mutableMapOf<String, Widget>()

override fun start() {
storage = VolumeStorage(vertx)
listenOnBoardsConfigSave()
listenOnConfigSave()
listenOnWidgetUpdate()
listenOnWidgetDelete()
loadBoardsConfig()
}

private fun listenOnBoardsConfigSave() = vertx
private fun listenOnConfigSave() = vertx
.eventBus()
.consumer<JsonObject>(CogboardConstants.EVENT_SAVE_BOARDS_CONFIG)
.consumer<JsonObject>(CC.EVENT_SAVE_BOARDS_CONFIG)
.handler { storage.saveBoardsConfig(it.body()) }

private fun listenOnWidgetUpdate() = vertx
.eventBus()
.consumer<JsonObject>(CC.EVENT_UPDATE_WIDGET_CONFIG)
.handler { createOrUpdate(it.body()) }

private fun listenOnWidgetDelete() = vertx
.eventBus()
.consumer<JsonObject>(CC.EVENT_DELETE_WIDGET_CONFIG)
.handler { delete(it.body()) }

private fun delete(config: JsonObject) {
val id = config.getString(CC.PROP_ID)

if (id != null) {
widgets.remove(id)?.stop()
LOGGER.info("Widget Deleted: $config")
} else {
LOGGER.error("Widget Delete | There is widget with no ID in configuration: $config")
}
}

private fun loadBoardsConfig() {
val config = storage.loadBoardsConfig()
if (config == CogboardConstants.errorResponse("Config not valid")) {
if (config == CC.errorResponse("Config not valid")) {
LOGGER.error("Boards config is invalid")
throw Validation.ValidationException("Boards config is invalid")
}
return config.getJsonObject(CogboardConstants.PROP_WIDGETS)
.getJsonObject(CogboardConstants.PROP_WIDGETS_BY_ID)

return config.getJsonObject(CC.PROP_WIDGETS)
.getJsonObject(CC.PROP_WIDGETS_BY_ID)
.forEach {
createOrUpdate(JsonObject(it.value.toString()))
}
}

fun createOrUpdate(config: JsonObject) {
private fun createOrUpdate(config: JsonObject) {
var newConfig = config
val id = config.getString(CogboardConstants.PROP_ID)
val id = config.getString(CC.PROP_ID)

if (id != null) {
widgets[id]?.let {
Expand All @@ -58,14 +82,14 @@ internal open class BoardsController : AbstractVerticle() {
}

private fun JsonObject.attachEndpoint() {
val endpointId = this.getString(CogboardConstants.PROP_ENDPOINT)
val endpointId = this.getString(CC.PROP_ENDPOINT)
endpointId?.let {
val endpoint = EndpointLoader.from(config(), endpointId).loadWithSensitiveData()
this.put(CogboardConstants.PROP_ENDPOINT, endpoint)
this.put(CC.PROP_ENDPOINT, endpoint)
}
}

companion object {
val LOGGER: Logger = LoggerFactory.getLogger(BoardsController::class.java)
val LOGGER: Logger = LoggerFactory.getLogger(BoardsAndWidgetsController::class.java)
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ class JenkinsJobWidget(vertx: Vertx, config: JsonObject) : AsyncWidget(vertx, co
private val path: String = config.getString("path", "")

override fun handleResponse(responseBody: JsonObject) {
responseBody.getJsonObject("lastBuild")?.let {
val status = if (it.getBoolean("building", false)) Widget.Status.IN_PROGRESS
else Widget.Status.from(it.getString("result", ""))
it.put("branch", extractBranchInfo(it))
it.put(CogboardConstants.PROP_URL, makePublic(it.getString(CogboardConstants.PROP_URL, "")))
val lastBuild = responseBody.getJsonObject("lastBuild")

if (lastBuild != null) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added error handling for JenkinsJob widget

val status = if (lastBuild.getBoolean("building", false)) Widget.Status.IN_PROGRESS
else Widget.Status.from(lastBuild.getString("result", ""))
lastBuild.put(CogboardConstants.PROP_ERROR_MESSAGE, "")
lastBuild.put("branch", extractBranchInfo(lastBuild))
lastBuild.put(CogboardConstants.PROP_URL, makePublic(lastBuild.getString(CogboardConstants.PROP_URL, "")))

send(JsonObject()
.put(CogboardConstants.PROP_STATUS, status)
.put(CogboardConstants.PROP_CONTENT, it))
.put(CogboardConstants.PROP_CONTENT, lastBuild))
} else {
sendConfigurationError("Config error: Wrong response.")
}
}

Expand Down Expand Up @@ -48,6 +53,8 @@ class JenkinsJobWidget(vertx: Vertx, config: JsonObject) : AsyncWidget(vertx, co
override fun updateState() {
if (url.isNotBlank() && path.isNotBlank()) {
httpGet(url = "$url$path/api/json?tree=lastBuild[$LAST_BUILD_PROPS]")
} else {
sendConfigurationError("Config error: URL or Path is blank.")
}
}

Expand Down
17 changes: 14 additions & 3 deletions cogboard-webapp/src/components/widgets/types/JenkinsJobWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import React from 'react';
import { string, number } from 'prop-types';

import { Caption, WidgetButton } from "../../styled";
import {Typography} from "@material-ui/core";

const JenkinsJobWidget = props => {
const { duration, displayName, url, timestamp, branch } = props;
const { duration, displayName, url, timestamp, branch, errorMessage } = props;
const ts = timestamp ? new Date(timestamp).toLocaleString() : '';
const dur = duration ? `${duration / 1000} [s]` : '';

if (errorMessage) {
return (
<Typography variant="h5">
{errorMessage}
</Typography>
);
}

return (
<>
<Caption>
Expand All @@ -31,11 +40,13 @@ JenkinsJobWidget.propTypes = {
displayName: string.isRequired,
url: string.isRequired,
timestamp: number.isRequired,
branch: string
branch: string,
errorMessage: string
};

JenkinsJobWidget.defaultProps = {
branch: 'unknown'
branch: 'unknown',
errorMessage: undefined
};

export default JenkinsJobWidget;
11 changes: 3 additions & 8 deletions knotx/conf/cogboard.conf
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
modules {
websocketsServer = "com.cognifide.cogboard.http.WebSocketsServer"
boardsAndWidgetsController = "com.cognifide.cogboard.config.controller.BoardsAndWidgetsController"
endpointsController = "com.cognifide.cogboard.config.controller.EndpointsController"
boardsController = "com.cognifide.cogboard.config.controller.BoardsController"
widgetsController = "com.cognifide.cogboard.config.controller.WidgetsController"
httpClient = "com.cognifide.cogboard.http.HttpClient"
}

Expand All @@ -12,15 +11,11 @@ config {
port = $ws_port
}

endpointsController.options.config {
boardsAndWidgetsController.options.config {
include required(file("/data/endpoints.json"))
}

boardsController.options.config {
include required(file("/data/endpoints.json"))
}

widgetsController.options.config {
endpointsController.options.config {
include required(file("/data/endpoints.json"))
}

Expand Down