Skip to content

Commit

Permalink
added support for the Dashboard View plugin
Browse files Browse the repository at this point in the history
[FIXES JENKINS-29146]
  • Loading branch information
daspilker committed Jan 3, 2016
1 parent c99904c commit 1aec407
Show file tree
Hide file tree
Showing 18 changed files with 444 additions and 8 deletions.
2 changes: 2 additions & 0 deletions docs/Home.md
Expand Up @@ -21,6 +21,8 @@ Browse the Jenkins issue tracker to see any [open issues](https://issues.jenkins

## Release Notes
* 1.42 (unreleased)
* Added support for the [Dashboard View Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Dashboard+View)
([JENKINS-29146](https://issues.jenkins-ci.org/browse/JENKINS-29146))
* Added support for the [Workflow Multibranch Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Workflow+Plugin)
([JENKINS-31671](https://issues.jenkins-ci.org/browse/JENKINS-31671),
[JENKINS-31719](https://issues.jenkins-ci.org/browse/JENKINS-31719))
Expand Down
@@ -0,0 +1,16 @@
dashboardView('example') {
jobs {
regex(/acme-.*/)
}
columns {
status()
weather()
buildButton()
}
topPortlets {
testStatisticsChart()
}
bottomPortlets {
testStatisticsGrid()
}
}
@@ -0,0 +1,7 @@
dashboardView('example') {
topPortlets {
testStatisticsChart {
displayName('Project A Test Stats')
}
}
}
@@ -0,0 +1,10 @@
dashboardView('example') {
rightPortlets {
testStatisticsGrid {
displayName('Project A Test Stats')
skippedColor('7F7F7F')
failureColor('FF0000')
successColor('00FF00')
}
}
}
Expand Up @@ -12,6 +12,7 @@ import javaposse.jobdsl.dsl.jobs.MultibranchWorkflowJob
import javaposse.jobdsl.dsl.views.BuildMonitorView
import javaposse.jobdsl.dsl.views.BuildPipelineView
import javaposse.jobdsl.dsl.views.CategorizedJobsView
import javaposse.jobdsl.dsl.views.DashboardView
import javaposse.jobdsl.dsl.views.DeliveryPipelineView
import javaposse.jobdsl.dsl.views.ListView
import javaposse.jobdsl.dsl.views.NestedView
Expand Down Expand Up @@ -173,6 +174,14 @@ abstract class JobParent extends Script implements DslFactory {
processView(name, CategorizedJobsView, closure)
}

/**
* @since 1.42
*/
@Override
DashboardView dashboardView(String name, @DslContext(DashboardView) Closure closure = null) {
processView(name, DashboardView, closure)
}

// this method cannot be private due to http://jira.codehaus.org/browse/GROOVY-6263
protected <T extends View> T processView(String name, Class<T> viewClass, Closure closure) {
checkNotNullOrEmpty(name, 'name must be specified')
Expand Down
Expand Up @@ -3,6 +3,7 @@ package javaposse.jobdsl.dsl
import javaposse.jobdsl.dsl.views.BuildMonitorView
import javaposse.jobdsl.dsl.views.BuildPipelineView
import javaposse.jobdsl.dsl.views.CategorizedJobsView
import javaposse.jobdsl.dsl.views.DashboardView
import javaposse.jobdsl.dsl.views.DeliveryPipelineView
import javaposse.jobdsl.dsl.views.ListView
import javaposse.jobdsl.dsl.views.NestedView
Expand Down Expand Up @@ -111,4 +112,21 @@ interface ViewFactory {
*/
@RequiresPlugin(id = 'categorized-view', minimumVersion = '1.8')
CategorizedJobsView categorizedJobsView(String name, @DslContext(CategorizedJobsView) Closure closure)

/**
* Creates or updates a dashboard / portal-like view.
*
* @see #dashboardView(java.lang.String, groovy.lang.Closure)
* @since 1.42
*/
@RequiresPlugin(id = 'dashboard-view', minimumVersion = '2.9.7')
DashboardView dashboardView(String name)

/**
* Creates or updates a dashboard / portal-like view.
* @since 1.42
*/
@RequiresPlugin(id = 'dashboard-view', minimumVersion = '2.9.7')
DashboardView dashboardView(String name, @DslContext(DashboardView) Closure closure)
}
@@ -0,0 +1,52 @@
package javaposse.jobdsl.dsl.views

import javaposse.jobdsl.dsl.DslContext
import javaposse.jobdsl.dsl.JobManagement
import javaposse.jobdsl.dsl.views.portlets.DashboardPortletContext

import static javaposse.jobdsl.dsl.ContextHelper.executeInContext

class DashboardView extends ListView {
DashboardView(JobManagement jobManagement) {
super(jobManagement)
}

/**
* Adds portlets to the top of the page.
*/
void topPortlets(@DslContext(DashboardPortletContext) Closure closure) {
addPortlets('topPortlets', closure)
}

/**
* Adds portlets to the bottom of the page.
*/
void bottomPortlets(@DslContext(DashboardPortletContext) Closure closure) {
addPortlets('bottomPortlets', closure)
}

/**
* Adds portlets to the left column.
*/
void leftPortlets(@DslContext(DashboardPortletContext) Closure closure) {
addPortlets('leftPortlets', closure)
}

/**
* Adds portlets to the right column.
*/
void rightPortlets(@DslContext(DashboardPortletContext) Closure closure) {
addPortlets('rightPortlets', closure)
}

protected void addPortlets(String elementName, Closure closure) {
DashboardPortletContext context = new DashboardPortletContext()
executeInContext(closure, context)

execute {
context.portletNodes.each { node ->
it / "$elementName" << node
}
}
}
}
Expand Up @@ -70,6 +70,14 @@ class NestedViewsContext extends AbstractContext implements ViewFactory {
processView(name, CategorizedJobsView, closure)
}

/**
* @since 1.42
*/
@Override
DashboardView dashboardView(String name, @DslContext(DashboardView) Closure closure = null) {
processView(name, DashboardView, closure)
}

private <T extends View> T processView(String name, Class<T> viewClass, Closure closure) {
Preconditions.checkNotNullOrEmpty(name, 'name must be specified')

Expand Down
@@ -0,0 +1,47 @@
package javaposse.jobdsl.dsl.views.portlets

import javaposse.jobdsl.dsl.Context
import javaposse.jobdsl.dsl.ContextHelper
import javaposse.jobdsl.dsl.DslContext

import java.security.SecureRandom

class DashboardPortletContext implements Context {
private static final Random RANDOM = new SecureRandom()

protected final List<Node> portletNodes = []

/**
* Adds a test statistics chart.
*/
void testStatisticsChart(@DslContext(TestStatisticsChartContext) Closure closure = null) {
TestStatisticsChartContext context = new TestStatisticsChartContext()
ContextHelper.executeInContext(closure, context)

portletNodes << new NodeBuilder().'hudson.plugins.view.dashboard.test.TestStatisticsChart' {
id(generatePortletId())
name(context.displayName ?: '')
}
}

/**
* Adds a test statistics grid.
*/
void testStatisticsGrid(@DslContext(TestStatisticsGridContext) Closure closure = null) {
TestStatisticsGridContext context = new TestStatisticsGridContext()
ContextHelper.executeInContext(closure, context)

portletNodes << new NodeBuilder().'hudson.plugins.view.dashboard.test.TestStatisticsPortlet' {
id(generatePortletId())
name(context.displayName ?: '')
useBackgroundColors(context.useBackgroundColors)
skippedColor(context.skippedColor ?: '')
successColor(context.successColor ?: '')
failureColor(context.failureColor ?: '')
}
}

private static String generatePortletId() {
"dashboard_portlet_${RANDOM.nextInt(32000)}"
}
}
@@ -0,0 +1,14 @@
package javaposse.jobdsl.dsl.views.portlets

import javaposse.jobdsl.dsl.Context

class TestStatisticsChartContext implements Context {
String displayName = 'Test Statistics Chart'

/**
* Sets the display name for the portlet. Defaults to {@code 'Test Statistics Chart'}.
*/
void displayName(String displayName) {
this.displayName = displayName
}
}
@@ -0,0 +1,52 @@
package javaposse.jobdsl.dsl.views.portlets

import javaposse.jobdsl.dsl.Context

class TestStatisticsGridContext implements Context {
String displayName = 'Test Statistics Grid'
boolean useBackgroundColors = false
String skippedColor = 'FDB813'
String successColor = '71E66D'
String failureColor = 'E86850'

/**
* Sets the display name for the portlet. Defaults to {@code 'Test Statistics Grid'}.
*/
void displayName(String displayName) {
this.displayName = displayName
}

/**
* If set, displays a colored background. Defaults to {@code false}.
*/
void useBackgroundColors(boolean useBackgroundColors = true) {
this.useBackgroundColors = useBackgroundColors
}

/**
* Sets the color for skipped tests as hex value. Defaults to {@code 'FDB813'}. Sets {@code useBackgroundColors} to
* {@code true}.
*/
void skippedColor(String skippedColor) {
useBackgroundColors()
this.skippedColor = skippedColor
}

/**
* Sets the color for successful tests as hex value. Defaults to {@code '71E66D'}. Sets {@code useBackgroundColors}
* to {@code true}.
*/
void successColor(String successColor) {
useBackgroundColors()
this.successColor = successColor
}

/**
* Sets the color for failed tests as hex value. Defaults to {@code 'E86850'}. Sets {@code useBackgroundColors} to
* {@code true}.
*/
void failureColor(String failureColor) {
useBackgroundColors()
this.failureColor = failureColor
}
}
@@ -0,0 +1,21 @@
<?xml version='1.0' encoding='UTF-8'?>
<hudson.plugins.view.dashboard.Dashboard>
<filterExecutors>false</filterExecutors>
<filterQueue>false</filterQueue>
<properties class="hudson.model.View$PropertyList"/>
<jobNames>
<comparator class="hudson.util.CaseInsensitiveComparator"/>
</jobNames>
<jobFilters/>
<columns/>
<recurse>false</recurse>
<useCssStyle>false</useCssStyle>
<includeStdJobList>false</includeStdJobList>
<hideJenkinsPanels>false</hideJenkinsPanels>
<leftPortletWidth>50%</leftPortletWidth>
<rightPortletWidth>50%</rightPortletWidth>
<leftPortlets/>
<rightPortlets/>
<topPortlets/>
<bottomPortlets/>
</hudson.plugins.view.dashboard.Dashboard>
Expand Up @@ -11,6 +11,7 @@ import javaposse.jobdsl.dsl.jobs.MultibranchWorkflowJob
import javaposse.jobdsl.dsl.views.BuildMonitorView
import javaposse.jobdsl.dsl.views.BuildPipelineView
import javaposse.jobdsl.dsl.views.CategorizedJobsView
import javaposse.jobdsl.dsl.views.DashboardView
import javaposse.jobdsl.dsl.views.DeliveryPipelineView
import javaposse.jobdsl.dsl.views.ListView
import javaposse.jobdsl.dsl.views.NestedView
Expand Down Expand Up @@ -198,6 +199,31 @@ class JobParentSpec extends Specification {
1 * jobManagement.requireMinimumPluginVersion('categorized-view', '1.8')
}

def 'should add dashboard view'() {
when:
View view = parent.dashboardView('test') {
description('foo')
}

then:
view.name == 'test'
view instanceof DashboardView
parent.referencedViews.contains(view)
view.node.description[0].text() == 'foo'
1 * jobManagement.requireMinimumPluginVersion('dashboard-view', '2.9.7')
}

def 'should add dashboard view without closure'() {
when:
View view = parent.dashboardView('test')

then:
view.name == 'test'
view instanceof DashboardView
parent.referencedViews.contains(view)
1 * jobManagement.requireMinimumPluginVersion('dashboard-view', '2.9.7')
}

def 'folder'() {
when:
Folder folder = parent.folder('test') {
Expand Down
@@ -1,6 +1,6 @@
package javaposse.jobdsl.dsl.views

class BuildMonitorViewSpec extends ListViewSpec {
class BuildMonitorViewSpec extends ListViewSpec<BuildMonitorView> {
def setup() {
view = new BuildMonitorView(jobManagement)
}
Expand Down
@@ -1,6 +1,6 @@
package javaposse.jobdsl.dsl.views

class CategorizedJobsViewSpec extends ListViewSpec {
class CategorizedJobsViewSpec extends ListViewSpec<CategorizedJobsView> {
def setup() {
view = new CategorizedJobsView(jobManagement)
}
Expand All @@ -23,7 +23,7 @@ class CategorizedJobsViewSpec extends ListViewSpec {

def 'do nothing on empty categorization criteria'() {
when:
((CategorizedJobsView) view).categorizationCriteria {
view.categorizationCriteria {
}

then:
Expand All @@ -35,7 +35,7 @@ class CategorizedJobsViewSpec extends ListViewSpec {

def 'group by regex with naming'() {
when:
((CategorizedJobsView) view).categorizationCriteria {
view.categorizationCriteria {
regexGroupingRule('regex', 'naming')
}

Expand All @@ -52,7 +52,7 @@ class CategorizedJobsViewSpec extends ListViewSpec {

def 'group by regex without naming'() {
when:
((CategorizedJobsView) view).categorizationCriteria {
view.categorizationCriteria {
regexGroupingRule('regex')
}

Expand All @@ -69,7 +69,7 @@ class CategorizedJobsViewSpec extends ListViewSpec {

def 'add more than one group'() {
when:
((CategorizedJobsView) view).categorizationCriteria {
view.categorizationCriteria {
regexGroupingRule('regex1', 'naming1')
regexGroupingRule('regex2', 'naming2')
}
Expand Down

0 comments on commit 1aec407

Please sign in to comment.