diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..5b8d5402 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [khmarbaise] \ No newline at end of file diff --git a/.gitignore b/.gitignore index d7f0f2a4..9dc43422 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ target .classpath .project .settings +test-output +.DS_Store diff --git a/.sdkmanrc b/.sdkmanrc new file mode 100644 index 00000000..4fc377de --- /dev/null +++ b/.sdkmanrc @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# +# Enable auto-env through the sdkman_auto_env config +# Add key=value pairs of SDKs to use below +java=8.0.252.hs-adpt diff --git a/.travis.yml b/.travis.yml index 44a4a166..6c57468f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,4 @@ --- -sudo: required - services: - docker @@ -9,9 +7,10 @@ services: # TODO: May be we need to think about this. # TODO: Make our own image and publish this to docker hub. before_install: + - docker --version - cd jenkins-client-it-docker - docker build --no-cache -t jenkins-with-plugins . - - docker run --name jenkins-for-testing -v "$(pwd)/jobs":/var/jenkins_home/jobs -d -p 8080:8080 -p 50000:50000 --env JENKINS_OPTS=--httpPort=8080 jenkins-with-plugins + - docker run --name jenkins-for-testing -v "$(pwd)/jobs":/var/jenkins_home/jobs -d -p 8080:8080 -p 50000:50000 --env JENKINS_OPTS=--httpPort=8080 jenkins-with-plugins - cd .. # We skip the installation step, @@ -22,7 +21,7 @@ install: true language: java script: - - mvn -Prun-its,run-docker-its clean verify --batch-mode + - mvn -Prun-its,run-docker-its clean verify --batch-mode --fail-at-end # Make sure we stop the container # and remove the container independent @@ -34,8 +33,10 @@ after_script: - docker rm -f -v jenkins-for-testing - docker rmi -f jenkins-with-plugins +# http://www.webupd8.org/2017/06/why-oracle-java-7-and-6-installers-no.html +# - oraclejdk8 is not supported anymore. jdk: - - oraclejdk7 + - openjdk8 cache: directories: - $HOME/.m2/repository diff --git a/Eclipse-JenkinsClient.xml b/Eclipse-JenkinsClient.xml index 6b46c4ff..dbeb09e2 100644 --- a/Eclipse-JenkinsClient.xml +++ b/Eclipse-JenkinsClient.xml @@ -6,23 +6,19 @@ - - + - - - @@ -35,7 +31,6 @@ - @@ -57,7 +52,6 @@ - @@ -89,7 +83,6 @@ - @@ -108,7 +101,6 @@ - @@ -124,7 +116,6 @@ - @@ -163,7 +154,6 @@ - @@ -173,7 +163,6 @@ - @@ -196,7 +185,6 @@ - @@ -208,17 +196,15 @@ - - - + @@ -272,7 +258,6 @@ - diff --git a/README.md b/README.md index 06b9a48e..8489b07c 100644 --- a/README.md +++ b/README.md @@ -3,28 +3,44 @@ [![MIT Licence](https://img.shields.io/github/license/jenkinsci/java-client-api.svg?label=License)](http://opensource.org/licenses/MIT) [![Maven Central](https://img.shields.io/maven-central/v/com.offbytwo.jenkins/jenkins-client.svg?label=Maven%20Central)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.offbytwo.jenkins%22%20a%3A%22jenkins-client%22) [![Build Status](https://travis-ci.org/jenkinsci/java-client-api.svg?branch=master)](https://travis-ci.org/jenkinsci/java-client-api) +[![Javadocs](https://javadoc.io/badge/com.offbytwo.jenkins/jenkins-client.svg?color=blue)](https://javadoc.io/doc/com.offbytwo.jenkins/jenkins-client) ## Important Note The Jenkins API Client For Java has now moved under the umbrella of the Jenkins GitHub Organization. +## What is the "Jenkins API Client for Java"? + +This library is just a piece of java code which uses the REST API of jenkins. +This means you can trigger builds, extract informations about jobs or builds +etc. The information you can extract will be represented in java objects which +you can reuse for other purposes or integrate this library into other parts for +a higher level of integration. + ## Getting Started -To get started add the following dependency to your project +If you like to use this library you need to add the library as a dependency +to your project. This can be done by using a Maven dependency like the following: ```xml com.offbytwo.jenkins jenkins-client - 0.3.5 + 0.3.8 ``` -Starting with Release 0.4.0 the groupId/artifactId will change (NOT YET DONE!) +This can also being done by defining a Gradle dependency like this: + +``` +compile 'com.offbytwo.jenkins:jenkins-client:0.3.8' +``` + +Starting with a future release 0.4.0 the groupId/artifactId will change (NOT YET DONE!) ```xml - NOT YET FINALIZED + NOT YET FINALIZED NOR RELEASED !!! org.jenkins-ci.lib java-client-api 0.4.0 @@ -72,16 +88,18 @@ To run integration tests simply start mvn -Prun-its clean verify ``` -There is also a separate project which contains [integration -tests][integration-tests] which are running with a special version of Jenkins +There is also a module which contains [integration tests][integration-tests] +which are running with a special version of Jenkins within a Docker container to check several aspects of the API which can't be covered by the usual integration tests. ## Release Notes -You can find details about the different releases in the [Release Notes](https://github.com/RisingOak/jenkins-client/blob/master/ReleaseNotes.md). +You can find details about the different releases in the [Release Notes](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md). - * [Release 0.4.0](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-040). + * [Release 0.3.9 NOT RELEASED YET](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-039). + * [Release 0.3.8](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-038). + * [Release 0.3.7](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-037). * [Release 0.3.6](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-036). * [Release 0.3.5](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-035). * [Release 0.3.4](https://github.com/jenkinsci/java-client-api/blob/master/ReleaseNotes.md#release-034). @@ -109,14 +127,23 @@ to add. Afterwards you can create an appropriate pull request. It is required if you want to get a Pull request to be integrated into please squash your commits into a single commit which references the issue in the -commit message. +commit message which looks like this: + +``` +Fixed #Issue + o Description. +``` + +This makes it simpler to merge it and this will also close the +appropriate issue automatically in one go. This make the life as +maintainer a little bit easier. A pull request has to fulfill only a single ticket and should never create/add/fix several issues in one, cause otherwise the history is hard to read and to understand and makes the maintenance of the issues and pull request hard or to be honest impossible. -Furthermore it is neccesary to create appropriate entries into the `ReleaseNotes.md` +Furthermore it is necessary to create appropriate entries into the `ReleaseNotes.md` file as well. @@ -134,3 +161,5 @@ http://jenkinsci.github.io/java-client-api/ Copyright (C) 2013, Cosmin Stejerean, Karl Heinz Marbaise, and contributors. Distributed under the MIT license: http://opensource.org/licenses/MIT + +[integration-tests]: https://github.com/jenkinsci/java-client-api/tree/master/jenkins-client-it-docker diff --git a/ReleaseNotes.md b/ReleaseNotes.md index 10241539..71176ed3 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -1,9 +1,583 @@ # Release Notes -## Release 0.3.6 (NOT RELEASED YET) +## Release 0.4.0 (NOT RELEASED YET) + + * [Fixed Issue 465][issue-465] + + * Remove asciidoctor site parts + * AS preparation for user guide. + + * [Fixed Issue 464][issue-464] + + * Replaced xml-apis with xerces-xmlParserAPI. + + * [Fixed Issue 309][issue-309] + + * Added possibility to get mode detailed data from Maven Modules from Jobs/Builds + + Thanks for that to [Jakub Zacek](https://github.com/dawon). + + * [Fixed Issue 395][issue-395] + + * Remove google guava lib + * Removed also the creation of the shaded artifact `stash` + cause we do not rely on Guava anymore. So you + can use the original artifact directly. + * This results in a bumping of the version + number cause it is a change which is breaking + with previous version 0.3.8. + + * [Fixed Issue 405][issue-405] + + * CVE-2018-14718 + * CVE-2018-14719 + * CVE-2018-14720 + * CVE-2018-14721 + * CVE-2018-19360 + * CVE-2018-19361 + * CVE-2018-19362 + + * [Fixed Issue 402][issue-402] + + Upgrade httpclient/httpmine/httpcore. + + * [Fixed Issue 401][issue-401] + + Upgrade JUnit + + * [Fixed Issue 400][issue-400] + + Upgrade assertj-core. + + * [Fixed Issue 399][issue-399] + + Upgrade Maven Plugins + + * [Fixed Issue 397][issue-397] + + Refactored Code Replaced UrlEscapers calls with EncodingUtils. + + * [Fixed Issue 396][issue-396] + + Add Unit Test for EncodingUtils. + + * [Fixed Issue 394][issue-394] + + Replace `Strings.isNullOrEmpty()` with self implemented code. + + * [Pull Request #386][pull-386] + + Add the crumbFlag as the 2nd parameter of getConsoleOutputText method + + * [JENKINS-56186][jissue-56186] + + Added labels to computers + + ```java + ComputerWithDetails computer = ... + for (ComputerLabel assignedLabel : computer.getAssignedLabels()) { + assignedLabel.getName() + } + ``` + + * [JENKINS-56585][jissue-56585] + + Change request method of `QuietDown()` to POST + + +## Release 0.3.8 + + * [Fixed Issue 289][issue-289] + + Added a build.stop() method which takes in a crumbFlag + + + * [Fixed Issue 301][issue-301] + + Decoupled JenkinsServer and JenkinsHttpClient by extracting JenkinsHttpClient + methods into public interface so that different implementations can be plugged + into JenkinsServer if required + + + * [Fixed Issue 298][issue-298] + + Added Closeable support to JenkinsServer and JenkinsHttpClient so that + underlying resources can be cleaned up when instances no longer required + + + * [JENKINS-46472][jissue-46472] + + Added ability to modify offline cause for offline computers. + + ```java + ComputerWithDetails computer = ... + if (!computer.getOffline()){ + computer.toggleOffline(); + computer.changeOfflineCause("Scheduled for termination"); + } + ``` + + * [JENKINS-46445][jissue-46445] + + Add support for both client TLS and basic authentication. + + ```java + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setSslcontext(sslContext); + JenkinsHttpClient client = new JenkinsHttpClient(uri, builder, username, password); + JenkinsServer jenkins = new JenkinsServer(client); + ``` + +* [Refactor Issue 291][issue-291] + + Useful utility methods refactored into utility classes. + + * [Fixed Issue 282][issue-282] + + `NullPointerException` may be thrown if `upstreamUrl` is `null` when + converting cause to `BuildCause` object. + + * [Fixed Issue 268][issue-268] + + NullPointerException is thrown unless isRunning() is called first. + + * [Fixed Issue 98][issue-98] + + Splitting fix made for jacoco reports from Jenkins #98. + Thanks to Shah, Prince. + + * [Fixed Issue 217][issue-217] + + Added new api for streaming build logs + + ```java + BuildWithDetails build = ... + // Get log with initial offset + int offset = 40; + String output = build.getConsoleOutputText(offset); + // Stream logs (when build is in progress) + BuildConsoleStreamListener buildListener = ... + build.streamConsoleOutput(listener, 3, 420); + ``` + + Thanks to Wojciech Trocki. + + * [Fixed Issue 222][issue-222] + + Fixed WARNING during build. + + * [Fixed Issue 220][issue-220] + + `getViews()` Do not use `api/json?depth=1` cause of timeout. + + * [Fixed Issue 244][issue-244] + + README.md Code grammar typos; Thanks to . + + * [Pull Request #229][pull-229] Fix race condition in JenkinsTriggerHelper + + Thanks for that to [Veske](https://github.com/Veske). + + * [Pull Request #239][pull-239] Fixed Bug in build method to prevent + building twice. + + Thanks for the pull request #239 from ladventure/master + + * [Pull Request #240][pull-240] Fixed code duplication. + + Thanks for the pull request #240 from Jonathan Bremer. + + * [Pull Request #247][pull-247] Add JavaDoc. + + Thanks to aisuke-yoshimoto + + * [Pull Request #262][pull-262] Fix typo. + + Thanks for Alberto Scotto. + +### API Changes + + * [Fixed Issue 243](https://github.com/jenkinsci/java-client-api/issues/243) + + Added new methods to JenkinsServer for stopping and restarting Jenkins. The methods are restart(Boolean crumbFlag), safeRestart(Boolean crumbFlag), exit(Boolean crumbFlag) and safeExit(Boolean crumbFlag) + + Thanks for that to [Chids](https://github.com/Chids-gs). + +## Release 0.3.7 + + * Changed Eclipse Formatting configuration. + +[Fixed Issue 186][issue-186] + + * Correctly escaping `{` and `}` for range syntax. + +[Fixed Issue 166][issue-166] + + * Added a supplemental package with classifier `apachehttp` which + includes the packages `org.apache.httpcomponents:httpclient` and + `org.apache.httpcomponents:httpcore`. + +### API Changes + + * [Fixed Issue 215][issue-215] + + The JenkinsServer class will return `JenkinsVersion` instead of String if you + call `getJenkinsVersion()`. + +```java +public class JenkinsVersion ... { + public boolean isGreaterThan(String version); + public boolean isGreaterOrEqual(String version); + public boolean isLessThan(String version); + public boolean isLessOrEqual(String version); + public boolean isEqualTo(String version); +} +``` + + The `JenkinsVersion` class can be used to compare different versions with + each other. + +```java +JenkinsVersion jv = new JenkinsVersion("1.651.1"); +assertThat(jv.isGreaterThan("1.651.1")).isFalse(); +``` + + + * [Fixed issue 184][issue-184] + + Based on the differences between getting a TestReport for a MavenJob type and + a freestyle job etc. you would have hit by getting 0 from `getTotalCount()` like + in the following code snippet: + +```java +JobWithDetails job = js.getJob("non-maven-test"); +Build lastCompletedBuild = job.getLastCompletedBuild(); +TestReport testReport = lastCompletedBuild.getTestReport(); +``` + + This is caused by the difference in the API of Jenkins which results in the following + for a MavenJob type: + + { + "_class" : "hudson.maven.reporters.SurefireAggregatedReport", + "failCount" : 0, + "skipCount" : 0, + "totalCount" : 489, + "urlName" : "testReport", + "childReports" : [ + { + "child" : { + "_class" : "hudson.maven.MavenBuild", + "number" : 2, + "url" : "http://localhost:27100/buildserver/job/maven-test/com.soebes.subversion.sapm$sapm/2/" + }, + "result" : { + "_class" : "hudson.tasks.junit.TestResult", + "testActions" : [ + + ], + "duration" : 0.009, + "empty" : false, + "failCount" : 0, + "passCount" : 489, + "skipCount" : 0, + "suites" : [ + { + "cases" : [ + + But for a non Maven job like freestyle job you will get the following: + + { + "_class" : "hudson.tasks.junit.TestResult", + "testActions" : [ + + ], + "duration" : 0.01, + "empty" : false, + "failCount" : 0, + "passCount" : 489, + "skipCount" : 0, + "suites" : [ + { + "cases" : [ + { + "testActions" : [ + + ], + "age" : 0, + "className" : "com.soebes.subversion.sapm.AccessRuleGroupTest", + "duration" : 0.003, + + This is exactly the cause for this result. + + The API has been enhanced to get the correct result. This can be achieved by calling + the following in cases where you have a non Maven job type. + +```java +TestResult testResult = lastCompletedBuild.getTestResult(); +``` + + This means you need to take care yourself if you are getting the test results from + Maven job type or non Maven job. (Future releases of the lib hopefully handle that + in a more convenient way). + + + + * [Fixed issue 209][issue-209] + + Consider Returning null from the "getTestReport()" of Build.java class for builds that Never run. + + The type `BUILD_HAS_NEVER_RUN` has been enhanced to return `TestReport.NO_TEST_REPORT` if you + call `getTestReport()` and return `BuildWithDetails.BUILD_HAS_NEVER_RUN` if you call `details()` on the + type. + + Furthermore `JobWithDetails` class has been enhanced with the following methods: + +```java +public class JobWithDetails ... { + public boolean hasFirstBuildRun(); + public boolean hasLastBuildRun(); + public boolean hasLastCompletedBuildRun(); + public boolean hasLastFailedBuildRun(); + public boolean hasLastStableBuildRun(); + public boolean hasLastSuccessfulBuildRun(); + public boolean hasLastUnstableBuildRun(); + public boolean hasLastUnsuccessfulBuildRun(); +} +``` + to make checking more convenient. The following code snippet + shows how you need to go before this change: + +```java +JobWithDetails job = server.getJob(jobName); +if (Build.BUILD_HAS_NEVER_RUN.equals(job.getLastBuild()) { + ... +} else { + // Now we can get the TestReport + job.getLastBuild().getTestReport(); +} +``` + + This can now be simplified to the following: + +```java +JobWithDetails job = server.getJob(jobName); +if (job.hasLastBuildRun()) { + // Now we can get the TestReport + job.getLastBuild().getTestReport(); +} else { +} +``` + + * [Fixed issue 211][issue-211] + + Added methods to update/clear the description of a job. + +```java +public class JobWithDetails .. { + public void updateDescription(String description); + public void updateDescription(String description, boolean crumbFlag); + public void clearDescription(); + public void clearDescription(boolean crumbFlag); +``` + + * [Fixed issue 188][issue-188] + + Added methods to update the description and the display name of a build. + +```java +public class BuildWithDetails .. { + public void updateDisplayNameAndDescription(String displayName, String description, boolean crumbFlag); + public void updateDisplayNameAndDescription(String displayName, String description); + public void updateDisplayName(String displayName, boolean crumbFlag); + public void updateDisplayName(String displayName); + public void updateDescription(String description, boolean crumbFlag); + public void updateDescription(String description); +} +``` + + * [Pull Request #206][pull-206] added runScript with `crumbFlag`. + + Thanks Rainer W. + +```java +public class JenkinsServer { + public String runScript(String script,boolean crumb); +} +``` + + * [Fixed issue 197][issue-197] Provide method for waiting until job has finished. + + Added a helper class to support this. + +```java +public class JenkinsTriggerHelper { + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName); + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, boolean crumbFlag); + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, Map params); + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, Map params,boolean crumbFlag); +} +``` + + * [Fixed Issue 104][issue-104] all build* methods now return consistently `QueueReference` + to make it possible to query for a queued build and if a build from the queue has + been cancelled or to see if a build is running. + +```java + public class Job extends BaseModel { + public QueueReference build(); + public QueueReference build(boolean crumbFlag); + public QueueReference build(Map params); + public QueueReference build(Map params, boolean crumbFlag); +} +``` + + * [Fixed Issue 203][issue-203] with [pull request #204][pull-204] + of RainerW + + Added methods getJobXml and updateJob with folder parameter. + +```java +public class JenkinsServer { + String getJobXml(FolderJob folder, String jobName); + void updateJob(FolderJob folder, String jobName, String jobXml, boolean crumbFlag); +} +``` + + * [Fixed Issue 207][issue-207] + + Added +```Java +public class QueueItem extends BaseModel { + + List getActions(); +} +``` + + and the new `QueueItemActions` will offer access to the + actions. + + * [Fixed Issue 38823][jissue-38823] + + Added an deleteJob method with a crumbFlag. + +```java +public class JenkinsServer { + deleteJob(FolderJob folder, String jobName, boolean crumbFlag); +} +``` + + * [Fixed Issue 38816][jissue-38816] + + Added several methods for creating/updating views. + +```java +public class JenkinsServer { + void createView(String viewName, String viewXml); + void createView(String viewName, String viewXml, Boolean crumbFlag); + void createView(FolderJob folder, String viewName, String viewXml); + void createView(FolderJob folder, String viewName, String viewXml, Boolean crumbFlag); + void updateView(String viewName, String viewXml); + void updateView(String viewName, String viewXml, boolean crumbFlag); +} +``` + + * [Fixed Issue 38787][jissue-38787] + + Returning null instead of IOException if view is not found in JenkinsServer.getView + + + * [Fixed Issue 201][issue-201] + + `MavenJobsWithDetails` is now in line with `JobWithDetails` and returns + `MavenBuild.BUILD_HAS_NEVER_RUN` in cases where the run has not taken + place yet. + +```java +public class MavenJobWithDetails { + public MavenBuild getLastBuild(); + public MavenBuild getLastCompletedBuild(); + public MavenBuild getLastFailedBuild(); + public MavenBuild getLastStableBuild(); + public MavenBuild getLastSuccessfulBuild(); + public MavenBuild getLastUnstableBuild(); + public MavenBuild getLastUnsuccessfulBuild(); +} +``` + + The `getBuilds()` method will return an empty list instead of `NULL` in cases no + builds exists. + + Fixed grammar and changed `Build.BUILD_HAS_NEVER_RAN` into `Build.BUILD_HAS_NEVER_RUN` + + * [Fixed Issue 202][issue-202] + +```java +public class MavenJobWithDetails { + public MavenBuild getFirstBuild(); +} +``` + + * [Fixed Issue 198][issue-198] + + Enhanced the `QueueItem` to add information about the task (QueueTask). + +```java +public class QueueItem { + public QueueTask getTask(); +} +``` + + * [Fixed Issue 200][issue-200] + + Added two methods to get all builds and a range of builds + so we are more in line wiht JobWithDetails. + +```java +public class MavenJobWithDetails { + public List getAllBuilds(); + public List getAllBuilds(Range range); +} +``` + +## Release 0.3.6 ### General Changes +[Fixed #182 remove duplicated code][issue-182] + +[Upgraded Maven Plugins][issue-167] + + * maven-release-plugin to 2.5.3 + * maven-site-plugin to 3.5.1 + * maven-shade-plugin to 2.4.3 + * maven-jar-plugin to 3.0.2 + * maven-source-plugin 3.0.1 + * maven-surefire/failsafe-plugin to 2.19.1 + * maven-resources-plugin to 3.0.1 + * Add missing maven-javadoc-plugin 2.10.4 + + +[Upgraded Maven Plugins JENKINS-35108][jissue-35108] + + * maven-clean-plugin to 3.0.0 + * maven-resources-plugin to 3.0.0 + * maven-jar-plugin to 3.0.0 + +[Fixed issue 176 HttpResponseException: Not Found (createJob)][issue-176] + Based on the wrong usage of the sub methods using crumbFlag=true instead + of crumbFlag=false. + +[Fixed issue 162][issue-162] + + JenkinsJob.details() produced NPE which has been got via view.getJob(). + +[Fixed issue 172][issue-172] + + The implementation `BuildWithDetails.getCauses()` could cause an + NPE which now has been imroved to prevent it. Thanks for hint + which brought me to reconsider the implementation. + Serveral issues fixed related to using logging framework [issue-161][issue-161], [issue-113][issue-113] and [JENKINS-35002][jissue-35002] @@ -13,10 +587,96 @@ As a user you can now decide which logging framework you would like to use. - [Changed the structure and integrated Docker IT][issue-160] +[Changed the structure and integrated Docker IT][issue-160] ### API Changes + The attributes of the following classes have been made + private. They are only accessible via getters/setters. + + `MavenArtifact`, `ComputerWithDetails`, `Executable`, `FolderJob`, + `JobWithDetails`, `MavenJobWithDetails`, `MavenModule`, `MavenModuleRecord`, + `PluginManager` + + [Fixed issue 179][issue-179] + + The `getTestReport` has been moved up from `MavenBuild` into + `Build` class. This makes the `TestReport` accessible + from any kind of build and not only from a Maven build. + + [Fixed issue 174][issue-174] + + `jenkins.getComputerSet().getComputer()` produced an error. + Changed `getComputer()` into `getComputers()` cause it returns + a list an not only a single computer. + Based on the above problem the `Executor` needed to be changed to + represent the correct data which is being returned. + +```java +public class ComputerSet { + public List getComputers(); +} +``` + +```java +public class Executor { + public Job getCurrentExecutable(); + public Job getCurrentWorkUnit(); +} +``` + + [Fixed issue 169 Add crumbFlag to renameJob][issue-169] + + Added supplemental `renameJob` method which supports crumbFlag. Furthermore + added `renameJob` which supports folder with and without `crumbFlag`. + So we now have the following methods to rename jobs: + +```java +public class JenkinsServer { + public void renameJob(String oldJobName, String newJobName) throws IOException; + public void renameJob(String oldJobName, String newJobName, Boolean crumbFlag) throws IOException; + public void renameJob(FolderJob folder, String oldJobName, String newJobName) throws IOException; + public void renameJob(FolderJob folder, String oldJobName, String newJobName, Boolean crumbFlag) throws IOException; +} +``` + + [Fixed issue 168 deletejobs in folder][issue-168] + + Added new method to delete a job within a folder. + +```java +public class JenkinsServer { + public void deleteJob(FolderJob folder, String jobName) throws IOException; +} +``` + + [Changing getLocalContext(), setLocalContext()][pull-163] + + The protected method `getLocalContext()` now returns + `HttpContext` instead of `BasicHttpContext`. + So the API has changed from the following: + +```java +public class JenkinsServer { + protected BasicHttpContext getLocalContext(); + protected void setLocalContext(BasicHttpContext localContext); + . +} +``` + + into this: + +```java +public class JenkinsServer { + protected HttpContext getLocalContext(); + protected void setLocalContext(HttpContext localContext); + . +} +``` + + Apart from that the visibility of the class `PreemptiveAuth` has been changed + from package private to public. + [Get Jenkins Version from http header][issue-90] ```java @@ -26,6 +686,15 @@ public class JenkinsServer { } ``` + [Added description for Job][issue-165] + +```java +public class JobWithDetails { + public String getDescription(); + . +} +``` + ## Release 0.3.5 @@ -428,9 +1097,11 @@ TestReport testReport = mavenJob.getLastSuccessfulBuild().getTestReport(); [issue-89]: https://github.com/jenkinsci/java-client-api/issues/89 [issue-90]: https://github.com/jenkinsci/java-client-api/issues/90 [issue-91]: https://github.com/jenkinsci/java-client-api/issues/91 +[issue-98]: https://github.com/jenkinsci/java-client-api/issues/98 [issue-104]: https://github.com/jenkinsci/java-client-api/issues/104 [issue-111]: https://github.com/jenkinsci/java-client-api/issues/111 [issue-116]: https://github.com/jenkinsci/java-client-api/issues/116 +[issue-104]: https://github.com/jenkinsci/java-client-api/issues/104 [issue-108]: https://github.com/jenkinsci/java-client-api/issues/108 [issue-113]: https://github.com/jenkinsci/java-client-api/issues/113 [issue-119]: https://github.com/jenkinsci/java-client-api/issues/119 @@ -450,7 +1121,69 @@ TestReport testReport = mavenJob.getLastSuccessfulBuild().getTestReport(); [issue-159]: https://github.com/jenkinsci/java-client-api/issues/159 [issue-160]: https://github.com/jenkinsci/java-client-api/issues/160 [issue-161]: https://github.com/jenkinsci/java-client-api/issues/161 +[issue-162]: https://github.com/jenkinsci/java-client-api/issues/162 +[issue-165]: https://github.com/jenkinsci/java-client-api/issues/165 +[issue-166]: https://github.com/jenkinsci/java-client-api/issues/166 +[issue-167]: https://github.com/jenkinsci/java-client-api/issues/167 +[issue-168]: https://github.com/jenkinsci/java-client-api/issues/168 +[issue-169]: https://github.com/jenkinsci/java-client-api/issues/169 +[issue-172]: https://github.com/jenkinsci/java-client-api/issues/172 +[issue-174]: https://github.com/jenkinsci/java-client-api/issues/174 +[issue-176]: https://github.com/jenkinsci/java-client-api/issues/176 +[issue-179]: https://github.com/jenkinsci/java-client-api/issues/179 +[issue-182]: https://github.com/jenkinsci/java-client-api/issues/182 +[issue-184]: https://github.com/jenkinsci/java-client-api/issues/184 +[issue-186]: https://github.com/jenkinsci/java-client-api/issues/186 +[issue-188]: https://github.com/jenkinsci/java-client-api/issues/188 +[issue-197]: https://github.com/jenkinsci/java-client-api/issues/197 +[issue-198]: https://github.com/jenkinsci/java-client-api/issues/198 +[issue-200]: https://github.com/jenkinsci/java-client-api/issues/200 +[issue-201]: https://github.com/jenkinsci/java-client-api/issues/201 +[issue-202]: https://github.com/jenkinsci/java-client-api/issues/202 +[issue-203]: https://github.com/jenkinsci/java-client-api/issues/203 +[issue-207]: https://github.com/jenkinsci/java-client-api/issues/207 +[issue-209]: https://github.com/jenkinsci/java-client-api/issues/209 +[issue-211]: https://github.com/jenkinsci/java-client-api/issues/211 +[issue-215]: https://github.com/jenkinsci/java-client-api/issues/215 +[issue-217]: https://github.com/jenkinsci/java-client-api/issues/217 +[issue-220]: https://github.com/jenkinsci/java-client-api/issues/220 +[issue-222]: https://github.com/jenkinsci/java-client-api/issues/222 +[issue-244]: https://github.com/jenkinsci/java-client-api/issues/244 +[issue-268]: https://github.com/jenkinsci/java-client-api/issues/268 +[issue-289]: https://github.com/jenkinsci/java-client-api/issues/289 +[issue-282]: https://github.com/jenkinsci/java-client-api/issues/282 +[issue-291]: https://github.com/jenkinsci/java-client-api/issues/291 +[issue-298]: https://github.com/jenkinsci/java-client-api/issues/298 +[issue-301]: https://github.com/jenkinsci/java-client-api/issues/301 +[issue-309]: https://github.com/jenkinsci/java-client-api/issues/309 +[issue-394]: https://github.com/jenkinsci/java-client-api/issues/394 +[issue-395]: https://github.com/jenkinsci/java-client-api/issues/395 +[issue-396]: https://github.com/jenkinsci/java-client-api/issues/396 +[issue-397]: https://github.com/jenkinsci/java-client-api/issues/397 +[issue-399]: https://github.com/jenkinsci/java-client-api/issues/399 +[issue-400]: https://github.com/jenkinsci/java-client-api/issues/400 +[issue-401]: https://github.com/jenkinsci/java-client-api/issues/401 +[issue-402]: https://github.com/jenkinsci/java-client-api/issues/402 +[issue-405]: https://github.com/jenkinsci/java-client-api/issues/405 +[issue-464]: https://github.com/jenkinsci/java-client-api/issues/464 [pull-123]: https://github.com/jenkinsci/java-client-api/pull/123 [pull-149]: https://github.com/jenkinsci/java-client-api/pull/149 [pull-158]: https://github.com/jenkinsci/java-client-api/pull/158 +[pull-163]: https://github.com/jenkinsci/java-client-api/pull/163 +[pull-204]: https://github.com/jenkinsci/java-client-api/pull/204 +[pull-206]: https://github.com/jenkinsci/java-client-api/pull/206 +[pull-229]: https://github.com/jenkinsci/java-client-api/pull/229 +[pull-239]: https://github.com/jenkinsci/java-client-api/pull/239 +[pull-240]: https://github.com/jenkinsci/java-client-api/pull/240 +[pull-247]: https://github.com/jenkinsci/java-client-api/pull/247 +[pull-262]: https://github.com/jenkinsci/java-client-api/pull/262 +[pull-386]: https://github.com/jenkinsci/java-client-api/pull/386 [jissue-35002]: https://issues.jenkins-ci.org/browse/JENKINS-35002 +[jissue-35108]: https://issues.jenkins-ci.org/browse/JENKINS-35108 +[jissue-38787]: https://issues.jenkins-ci.org/browse/JENKINS-38787 +[jissue-38816]: https://issues.jenkins-ci.org/browse/JENKINS-38816 +[jissue-38823]: https://issues.jenkins-ci.org/browse/JENKINS-38823 +[jissue-46445]: https://issues.jenkins-ci.org/browse/JENKINS-46445 +[jissue-46472]: https://issues.jenkins-ci.org/browse/JENKINS-46472 +[jissue-56186]: https://issues.jenkins-ci.org/browse/JENKINS-56186 +[jissue-56585]: https://issues.jenkins-ci.org/browse/JENKINS-56585 diff --git a/jenkins-client-it-docker/Dockerfile b/jenkins-client-it-docker/Dockerfile index 00abeaa4..7b4118a6 100644 --- a/jenkins-client-it-docker/Dockerfile +++ b/jenkins-client-it-docker/Dockerfile @@ -1,4 +1,4 @@ -FROM jenkins:2.0 +FROM jenkins:1.651.3 MAINTAINER "docker@soebes.de" COPY ./plugins.txt /usr/share/jenkins/ref/plugins.txt COPY ./config.xml /usr/share/jenkins/ref/config.xml diff --git a/jenkins-client-it-docker/README.md b/jenkins-client-it-docker/README.md index 50703eb6..c94270af 100644 --- a/jenkins-client-it-docker/README.md +++ b/jenkins-client-it-docker/README.md @@ -15,10 +15,6 @@ Those plugins are needed for the integration tests. TODO ---- - * I need to find a way to get the jar which is being built by - the [Jenkins API client for Java][1]. May be i need to move - the integration test part to the main project. - * Create a docker image which contains [JaCoCo plugin installed][pr-99]. * Create a docker image which contains [TestNG plugin installed][pr-99]. @@ -36,7 +32,7 @@ STATUS Proof of Concept how to write integration tests based on Docker image for Jenkins. -[1]: https://github.com/RisingOak/jenkins-client -[issue-119]: https://github.com/RisingOak/jenkins-client/issues/119 -[pr-99]: https://github.com/RisingOak/jenkins-client/pull/99 -[pr-127]: https://github.com/RisingOak/jenkins-client/pull/127 +[1]: https://github.com/jenkinsci/java-client-api/ +[issue-119]: https://github.com/jenkinsci/java-client-api//issues/119 +[pr-99]: https://github.com/jenkinsci/java-client-api//pull/99 +[pr-127]: https://github.com/jenkinsci/java-client-api//pull/127 diff --git a/jenkins-client-it-docker/config.xml b/jenkins-client-it-docker/config.xml index a9e06f6f..6bf7ac5f 100644 --- a/jenkins-client-it-docker/config.xml +++ b/jenkins-client-it-docker/config.xml @@ -38,6 +38,28 @@ false + + + Test-View + false + false + + + + test + + + + + + + + + + + + false + All 50000 @@ -45,4 +67,4 @@ true - \ No newline at end of file + diff --git a/jenkins-client-it-docker/jobs/test/config.xml b/jenkins-client-it-docker/jobs/test/config.xml index 91a44a42..885fc61d 100644 --- a/jenkins-client-it-docker/jobs/test/config.xml +++ b/jenkins-client-it-docker/jobs/test/config.xml @@ -1,16 +1,9 @@ - + This is the description with umlauts äöü false - - - 0 - 0 - false - project - - + true false diff --git a/jenkins-client-it-docker/plugins.txt b/jenkins-client-it-docker/plugins.txt index 86807b85..e802c9d0 100644 --- a/jenkins-client-it-docker/plugins.txt +++ b/jenkins-client-it-docker/plugins.txt @@ -2,8 +2,9 @@ timestamper:1.7.2 credentials:1.24 junit:1.10 token-macro:1.12.1 -throttle-concurrents:1.8.4 +throttle-concurrents:1.9.0 jacoco:1.0.19 job-dsl:1.41 config-file-provider:2.10.0 testng-plugin:1.10 +cloudbees-folder: 5.12 diff --git a/jenkins-client-it-docker/pom.xml b/jenkins-client-it-docker/pom.xml index 38234530..11e6b485 100644 --- a/jenkins-client-it-docker/pom.xml +++ b/jenkins-client-it-docker/pom.xml @@ -5,16 +5,13 @@ ~ Distributed under the MIT license: http://opensource.org/licenses/MIT --> - + 4.0.0 com.offbytwo.jenkins jenkins-client-parent - 0.3.6-SNAPSHOT + 0.4.0-SNAPSHOT jenkins-client-it-docker @@ -46,11 +43,6 @@ assertj-core test - - com.google.guava - guava - test - org.apache.httpcomponents httpclient @@ -71,10 +63,17 @@ org.apache.logging.log4j log4j-slf4j-impl test - + + + org.apache.maven.plugins + maven-gpg-plugin + + true + + org.apache.maven.plugins maven-jar-plugin @@ -82,22 +81,23 @@ true + + org.apache.maven.plugins + maven-install-plugin + + true + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + - - http://192.168.99.100:8080 - - - - - env.TRAVIS - - - - http://127.0.0.1:8080/ - - run-docker-its diff --git a/jenkins-client-it-docker/src/site/site.xml b/jenkins-client-it-docker/src/site/site.xml new file mode 100644 index 00000000..39d61857 --- /dev/null +++ b/jenkins-client-it-docker/src/site/site.xml @@ -0,0 +1,12 @@ + + + + + + + + + + diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java index 6321be93..bb0bd99f 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/AbstractJenkinsIntegrationCase.java @@ -8,63 +8,56 @@ import com.offbytwo.jenkins.JenkinsServer; -@Listeners( { MethodListener.class } ) -public class AbstractJenkinsIntegrationCase -{ +@Listeners({ MethodListener.class }) +public class AbstractJenkinsIntegrationCase { protected static JenkinsServer jenkinsServer; /** - * The time we wait until we break the beforeSuit method to prevent to wait forever. + * The time we wait until we break the beforeSuit method to prevent to wait + * forever. */ - public static final Long TIME_OUT_MILLISECONDS = TimeUnit.MILLISECONDS.convert( 1L, TimeUnit.MINUTES ); + public static final Long TIME_OUT_MILLISECONDS = TimeUnit.MILLISECONDS.convert(1L, TimeUnit.MINUTES); @BeforeSuite - public void waitUntilJenkinsHasBeenStartedUp() - throws TimeoutException - { + public void waitUntilJenkinsHasBeenStartedUp() throws TimeoutException { final long start = System.currentTimeMillis(); - jenkinsServer = new JenkinsServer( Constant.JENKINS_URI ); - System.out.print( "Wait until Jenkins is started..." ); - while ( !jenkinsServer.isRunning() && !timeOut( start ) ) - { - try - { - System.out.print( "." ); - Thread.sleep( TimeUnit.MILLISECONDS.convert( 1L, TimeUnit.SECONDS ) ); - } - catch ( InterruptedException e ) - { + jenkinsServer = new JenkinsServer(Constant.JENKINS_URI); + System.out.print("Wait until Jenkins is started..."); + while (!jenkinsServer.isRunning() && !timeOut(start)) { + try { + System.out.print("."); + Thread.sleep(TimeUnit.MILLISECONDS.convert(1L, TimeUnit.SECONDS)); + } catch (InterruptedException e) { e.printStackTrace(); } } - if ( !jenkinsServer.isRunning() && timeOut( start ) ) - { - System.out.println( "Failure." ); - throw new TimeoutException( "Jenkins startup check has failed. Took more than one minute." ); + if (!jenkinsServer.isRunning() && timeOut(start)) { + System.out.println("Failure."); + throw new TimeoutException("Jenkins startup check has failed. Took more than one minute."); } - System.out.println( "done." ); + System.out.println("done."); } /** - * Check if we have reached timeout related to the {@link #TIME_OUT_MILLISECONDS}. + * Check if we have reached timeout related to the + * {@link #TIME_OUT_MILLISECONDS}. * - * @param start The start time in milliseconds. + * @param start + * The start time in milliseconds. * @return true if timeout false otherwise. */ - private boolean timeOut( final long start ) - { + private boolean timeOut(final long start) { boolean result = false; long elapsed = System.currentTimeMillis() - start; - if ( elapsed >= TIME_OUT_MILLISECONDS ) - { + if (elapsed >= TIME_OUT_MILLISECONDS) { result = true; } return result; } - + } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Constant.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Constant.java index 066e2a87..400a85ac 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Constant.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Constant.java @@ -6,10 +6,8 @@ public final class Constant { /** * The URL for the running Jenkins server (currently a Docker image). On - * travis it is localhost (127.0.0.1) on my machine it is different. - * At the moment it is solved by a profile in pom..but could that somehow - * identified by docker itself ? + * travis it is localhost (127.0.0.1). */ - public static final URI JENKINS_URI = URI.create(System.getProperty("docker.container.network")); + public static final URI JENKINS_URI = URI.create("http://127.0.0.1:8080/"); } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartedIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartedIT.java index 99cab9c4..697e9349 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartedIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartedIT.java @@ -10,27 +10,21 @@ import com.offbytwo.jenkins.model.ComputerWithDetails; import com.offbytwo.jenkins.model.JobWithDetails; -@Test( dependsOnGroups = { Groups.EXECUTOR_STARTING_GROUP }, groups = { Groups.EXECUTOR_STARTED_GROUP } ) -public class ExecutorStartedIT - extends AbstractJenkinsIntegrationCase -{ +@Test(dependsOnGroups = { Groups.EXECUTOR_STARTING_GROUP }, groups = { Groups.EXECUTOR_STARTED_GROUP }) +public class ExecutorStartedIT extends AbstractJenkinsIntegrationCase { private JobWithDetails job; @BeforeMethod - public void beforeMethod() - throws IOException - { - job = jenkinsServer.getJob( "test" ); - assertThat( job ).isNotNull(); + public void beforeMethod() throws IOException { + job = jenkinsServer.getJob("test"); + assertThat(job).isNotNull(); } @Test - public void shouldTriggerJobTest() - throws IOException - { - ComputerWithDetails computerWithDetailsAfterStarting = jenkinsServer.getComputerSet().getComputer().get( 0 ); - assertThat( computerWithDetailsAfterStarting.getOffline() ).isFalse(); + public void shouldTriggerJobTest() throws IOException { + ComputerWithDetails computerWithDetailsAfterStarting = jenkinsServer.getComputerSet().getComputers().get(0); + assertThat(computerWithDetailsAfterStarting.getOffline()).isFalse(); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartingIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartingIT.java index c650c7f5..e78e55f7 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartingIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/ExecutorStartingIT.java @@ -10,31 +10,25 @@ import com.offbytwo.jenkins.model.ComputerWithDetails; import com.offbytwo.jenkins.model.JobWithDetails; -@Test( dependsOnGroups = { Groups.NO_EXECUTOR_GROUP }, groups = { Groups.EXECUTOR_STARTING_GROUP } ) -public class ExecutorStartingIT - extends AbstractJenkinsIntegrationCase -{ +@Test(dependsOnGroups = { Groups.NO_EXECUTOR_GROUP_FOLDER }, groups = { Groups.EXECUTOR_STARTING_GROUP }) +public class ExecutorStartingIT extends AbstractJenkinsIntegrationCase { private JobWithDetails job; @BeforeMethod - public void beforeMethod() - throws IOException - { - job = jenkinsServer.getJob( "test" ); - assertThat( job ).isNotNull(); + public void beforeMethod() throws IOException { + job = jenkinsServer.getJob("test"); + assertThat(job).isNotNull(); } @Test - public void shouldTriggerJobTest() - throws IOException - { - ComputerWithDetails computerWithDetails = jenkinsServer.getComputerSet().getComputer().get( 0 ); + public void shouldTriggerJobTest() throws IOException { + ComputerWithDetails computerWithDetails = jenkinsServer.getComputerSet().getComputers().get(0); computerWithDetails.toggleOffline(); - ComputerWithDetails computerWithDetailsAfterStarting = jenkinsServer.getComputerSet().getComputer().get( 0 ); + ComputerWithDetails computerWithDetailsAfterStarting = jenkinsServer.getComputerSet().getComputers().get(0); - assertThat( computerWithDetailsAfterStarting.getOffline() ).isFalse(); + assertThat(computerWithDetailsAfterStarting.getOffline()).isFalse(); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Groups.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Groups.java index 79557371..c4f6e49d 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Groups.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/Groups.java @@ -1,14 +1,15 @@ package com.offbytwo.jenkins.integration; -public final class Groups -{ +public final class Groups { /** * The group for test which expect that no executor is running. */ public static final String NO_EXECUTOR_GROUP = "NoExecutorStarted"; + public static final String NO_EXECUTOR_GROUP_FOLDER = "NoExecutorStartedFolder"; + public static final String EXECUTOR_STARTING_GROUP = "ExecutorStarting"; - + public static final String EXECUTOR_STARTED_GROUP = "ExecutorStarted"; } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedFolderIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedFolderIT.java new file mode 100644 index 00000000..b3ce0f19 --- /dev/null +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedFolderIT.java @@ -0,0 +1,20 @@ +package com.offbytwo.jenkins.integration; + +import java.io.IOException; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +@Test(dependsOnGroups = { Groups.NO_EXECUTOR_GROUP }, groups = { Groups.NO_EXECUTOR_GROUP_FOLDER }) +public class NoExecutorStartedFolderIT extends AbstractJenkinsIntegrationCase { + + @BeforeMethod + public void beforeMethod() throws IOException { + } + + @Test + public void createFolderShouldCreateTheFolder() throws IOException { + jenkinsServer.createFolder("First-Folder"); + } + +} diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputerSetIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputerSetIT.java index 0bd2b445..4fe4fc6a 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputerSetIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputerSetIT.java @@ -11,43 +11,35 @@ import com.offbytwo.jenkins.model.ComputerSet; import com.offbytwo.jenkins.model.ComputerWithDetails; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetComputerSetIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetComputerSetIT extends AbstractJenkinsIntegrationCase { private ComputerSet computerSet; @BeforeMethod - public void beforeMethod() - throws IOException - { + public void beforeMethod() throws IOException { computerSet = jenkinsServer.getComputerSet(); } @Test - public void shouldGetNameOfMasterNode() - { - List computers = computerSet.getComputer(); - assertThat( computers ).hasSize( 1 ); - assertThat( computers.get( 0 ).getDisplayName() ).isEqualTo( "master" ); + public void shouldGetNameOfMasterNode() { + List computers = computerSet.getComputers(); + assertThat(computers).hasSize(1); + assertThat(computers.get(0).getDisplayName()).isEqualTo("master"); } @Test - public void getTotalExecutorsShouldReturnZero() - { - assertThat( computerSet.getTotalExecutors() ).isEqualTo( 0 ); + public void getTotalExecutorsShouldReturnZero() { + assertThat(computerSet.getTotalExecutors()).isEqualTo(0); } @Test - public void getBusyExecutorsShouldReturnZero() - { - assertThat( computerSet.getBusyExecutors() ).isEqualTo( 0 ); + public void getBusyExecutorsShouldReturnZero() { + assertThat(computerSet.getBusyExecutors()).isEqualTo(0); } @Test - public void getDisplayNameShouldReturnNodes() - { - assertThat( computerSet.getDisplayName() ).isEqualTo( "Nodes" ); + public void getDisplayNameShouldReturnNodes() { + assertThat(computerSet.getDisplayName()).isEqualTo("Nodes"); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersIT.java index 3dc3f125..1538868d 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersIT.java @@ -10,30 +10,24 @@ import com.offbytwo.jenkins.model.Computer; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetComputersIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetComputersIT extends AbstractJenkinsIntegrationCase { private Map computers; @BeforeMethod - public void beforeMethod() - throws IOException - { + public void beforeMethod() throws IOException { computers = jenkinsServer.getComputers(); } @Test - public void numberOfComputersIsEqualOne() - { - assertThat( computers ).hasSize( 1 ); + public void numberOfComputersIsEqualOne() { + assertThat(computers).hasSize(1); } @Test - public void getNameShouldReturnMaster() - { + public void getNameShouldReturnMaster() { String key = computers.keySet().iterator().next(); - assertThat( computers.get( key ).getDisplayName() ).isEqualTo( "master" ); + assertThat(computers.get(key).getDisplayName()).isEqualTo("master"); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersWithDetailsIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersWithDetailsIT.java index 76856fc7..9a22eb0a 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersWithDetailsIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetComputersWithDetailsIT.java @@ -9,78 +9,64 @@ import com.offbytwo.jenkins.model.ComputerWithDetails; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetComputersWithDetailsIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetComputersWithDetailsIT extends AbstractJenkinsIntegrationCase { private ComputerWithDetails computerWithDetails; @BeforeMethod - public void beforeMethod() - throws IOException - { - computerWithDetails = jenkinsServer.getComputerSet().getComputer().get( 0 ); + public void beforeMethod() throws IOException { + computerWithDetails = jenkinsServer.getComputerSet().getComputers().get(0); } @Test - public void getIdleShouldReturnTrue() - { - assertThat( computerWithDetails.getIdle() ).isTrue(); + public void getIdleShouldReturnTrue() { + assertThat(computerWithDetails.getIdle()).isTrue(); } @Test - public void getJnlpShouldReturnTrue() - { - assertThat( computerWithDetails.getJnlp() ).isNull(); + public void getJnlpShouldReturnTrue() { + assertThat(computerWithDetails.getJnlp()).isNull(); } @Test - public void getExecutorsShouldReturnTwo() - { - assertThat( computerWithDetails.getExecutors() ).hasSize( 2 ); + public void getExecutorsShouldReturnTwo() { + assertThat(computerWithDetails.getExecutors()).hasSize(2); } @Test - public void getLaunchSupportedShouldReturnTrue() - { - assertThat( computerWithDetails.getLaunchSupported() ).isTrue(); + public void getLaunchSupportedShouldReturnTrue() { + assertThat(computerWithDetails.getLaunchSupported()).isTrue(); } @Test - public void getManualLanuchShouldReturnTrue() - { - assertThat( computerWithDetails.getManualLaunchAllowed() ).isTrue(); + public void getManualLanuchShouldReturnTrue() { + assertThat(computerWithDetails.getManualLaunchAllowed()).isTrue(); } @Test - public void getNumExecutorsShouldReturnTwo() - { - assertThat( computerWithDetails.getNumExecutors() ).isEqualTo( 2 ); + public void getNumExecutorsShouldReturnTwo() { + assertThat(computerWithDetails.getNumExecutors()).isEqualTo(2); } @Test - public void getOffLineShouldReturnTrue() - { - assertThat( computerWithDetails.getOffline() ).isTrue(); + public void getOffLineShouldReturnTrue() { + assertThat(computerWithDetails.getOffline()).isTrue(); } @Test - public void getOfflineReasonShouldReturnNonNull() - { - assertThat( computerWithDetails.getOfflineCauseReason() ).isEqualTo( "Manually turned off" ); + public void getOfflineReasonShouldReturnNonNull() { + assertThat(computerWithDetails.getOfflineCauseReason()).isEqualTo("Manually turned off"); } @Test - public void getTemporarilyOfflineShouldReturnTrue() throws IOException - { - assertThat( computerWithDetails.getTemporarilyOffline() ).isTrue(); + public void getTemporarilyOfflineShouldReturnTrue() throws IOException { + assertThat(computerWithDetails.getTemporarilyOffline()).isTrue(); } @Test - public void getOfflineCauseShouldReturnNonNull() throws IOException - { - assertThat( computerWithDetails.getOfflineCause() ).isNotNull(); + public void getOfflineCauseShouldReturnNonNull() throws IOException { + assertThat(computerWithDetails.getOfflineCause()).isNotNull(); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobDetailsIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobDetailsIT.java new file mode 100644 index 00000000..1f01d782 --- /dev/null +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobDetailsIT.java @@ -0,0 +1,40 @@ +package com.offbytwo.jenkins.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.util.List; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.offbytwo.jenkins.model.BuildCause; +import com.offbytwo.jenkins.model.BuildWithDetails; +import com.offbytwo.jenkins.model.JobWithDetails; + +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetJobDetailsIT extends AbstractJenkinsIntegrationCase { + + private JobWithDetails job; + + @BeforeMethod + public void beforeMethod() throws IOException { + job = jenkinsServer.getJob("test"); + } + + @Test + public void shouldCheckTheBuildCause() throws IOException { + BuildWithDetails details = job.getFirstBuild().details(); + List causes = details.getCauses(); + assertThat(causes).hasSize(1); + BuildCause buildCause = causes.get(0); + + assertThat(buildCause.getShortDescription()).isEqualTo("Started by user anonymous"); + assertThat(buildCause.getUserName()).isEqualTo("anonymous"); + assertThat(buildCause.getUpstreamBuild()).isEqualTo(0); + assertThat(buildCause.getUpstreamProject()).isNull(); + assertThat(buildCause.getUserId()).isNull(); + + } + +} diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobIT.java index 93ad3729..35c2aeac 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobIT.java @@ -7,120 +7,122 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import com.google.common.base.Joiner; import com.offbytwo.jenkins.model.Build; import com.offbytwo.jenkins.model.BuildResult; import com.offbytwo.jenkins.model.BuildWithDetails; import com.offbytwo.jenkins.model.JobWithDetails; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetJobIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetJobIT extends AbstractJenkinsIntegrationCase { private JobWithDetails job; @BeforeMethod - public void beforeMethod() - throws IOException - { - job = jenkinsServer.getJob( "test" ); + public void beforeMethod() throws IOException { + job = jenkinsServer.getJob("test"); } @Test - public void getBuildsShouldContainOnlyASingleBuild() - { - assertThat( job.getBuilds() ).hasSize( 1 ); - } - - private void checkJob( BuildWithDetails details ) throws IOException - { - assertThat( details.getResult() ).isEqualTo( BuildResult.SUCCESS ); - assertThat( details.isBuilding() ).isFalse(); - assertThat( details.getNumber() ).isEqualTo( 1 ); - assertThat( details.getQueueId() ).isEqualTo( 1 ); - assertThat( details.getChangeSet() ).isNotNull(); + public void getBuildsShouldContainOnlyASingleBuild() { + assertThat(job.getBuilds()).hasSize(1); + } + + private void checkJob(BuildWithDetails details) throws IOException { + assertThat(details.getResult()).isEqualTo(BuildResult.SUCCESS); + assertThat(details.isBuilding()).isFalse(); + assertThat(details.getNumber()).isEqualTo(1); + assertThat(details.getQueueId()).isEqualTo(1); + assertThat(details.getChangeSet()).isNotNull(); // FIXME: Currently getActions is only a plain List should be improved. - assertThat( details.getActions() ).isNotNull(); - assertThat( details.getFullDisplayName() ).isEqualTo( "test #1" ); - assertThat( details.getDescription() ).isNull(); - assertThat( details.getId() ).isEqualTo( "1" ); + assertThat(details.getActions()).isNotNull(); + assertThat(details.getFullDisplayName()).isEqualTo("test #1"); + assertThat(details.getDescription()).isNull(); + assertThat(details.getId()).isEqualTo("1"); // FIXME: Think hard about this, cause this is only valid for this - // special case which is committed in the current state of the git repository + // special case which is committed in the current state of the git + // repository // for this job. - assertThat( details.getDuration() ).isEqualTo( 236 ); - assertThat( details.getEstimatedDuration() ).isEqualTo( 236 ); - - String[] expectedOutputLines = { - "Started by user anonymous", - "Building in workspace /var/jenkins_home/jobs/test/workspace", - "[workspace] $ /bin/sh -xe /tmp/hudson2556403647634111927.sh", - "+ echo test", - "test", - "Finished: SUCCESS", - "" - }; - String expectedOutput = Joiner.on( "\r\n" ).join( expectedOutputLines ) ; - //Hint: It looks like the consoleOutputText contains CR+LF + assertThat(details.getDuration()).isEqualTo(236); + assertThat(details.getEstimatedDuration()).isEqualTo(236); + + String[] expectedOutputLines = { "Started by user anonymous", + "Building in workspace /var/jenkins_home/jobs/test/workspace", + "[workspace] $ /bin/sh -xe /tmp/hudson2556403647634111927.sh", "+ echo test", "test", + "Finished: SUCCESS", "" }; + String expectedOutput = String.join("\r\n", expectedOutputLines); + // Hint: It looks like the consoleOutputText contains CR+LF String resultingOutput = details.getConsoleOutputText(); - assertThat( resultingOutput ).isEqualTo( expectedOutput); + assertThat(resultingOutput).isEqualTo(expectedOutput); + } + + @Test + public void getFirstBuildShouldNotBeNull() throws IOException { + assertThat(job.getFirstBuild()).isNotNull(); + checkJob(job.getFirstBuild().details()); + } + + @Test + public void getLastBuildShouldNotBeNull() throws IOException { + assertThat(job.getLastBuild()).isNotNull(); + checkJob(job.getLastBuild().details()); + } + + @Test + public void getLastCompletedBuldShouldNotBeNull() throws IOException { + assertThat(job.getLastCompletedBuild()).isNotNull(); + checkJob(job.getLastCompletedBuild().details()); + } + + @Test + public void getLastFailedBuildShouldBeBUILD_HAS_NEVER_RAN() { + assertThat(job.getLastFailedBuild()).isEqualTo(Build.BUILD_HAS_NEVER_RUN); } @Test - public void getFirstBuildShouldNotBeNull() - throws IOException - { - assertThat( job.getFirstBuild() ).isNotNull(); - checkJob( job.getFirstBuild().details() ); + public void hasLastBuildShouldBeTrue() { + assertThat(job.hasLastBuildRun()).isTrue(); } @Test - public void getLastBuildShouldNotBeNull() - throws IOException - { - assertThat( job.getLastBuild() ).isNotNull(); - checkJob( job.getLastBuild().details() ); + public void hasLastFailedBuildShouldBeFalse() { + assertThat(job.hasLastFailedBuildRun()).isFalse(); } @Test - public void getLastCompletedBuldShouldNotBeNull() - throws IOException - { - assertThat( job.getLastCompletedBuild() ).isNotNull(); - checkJob( job.getLastCompletedBuild().details() ); + public void getLastStableBuildShouldNotBeNull() throws IOException { + assertThat(job.getLastStableBuild()).isNotNull(); + checkJob(job.getLastStableBuild().details()); } @Test - public void getLastFailedBuildShouldBeBUILD_HAS_NEVER_RAN() - { - assertThat( job.getLastFailedBuild() ).isEqualTo(Build.BUILD_HAS_NEVER_RAN); + public void getLastSuccessfulBuildShouldNotBeNull() throws IOException { + assertThat(job.getLastSuccessfulBuild()).isNotNull(); + checkJob(job.getLastSuccessfulBuild().details()); } @Test - public void getLastStableBuildShouldNotBeNull() - throws IOException - { - assertThat( job.getLastStableBuild() ).isNotNull(); - checkJob( job.getLastStableBuild().details() ); + public void getLastUnstableBuildShouldBeBUILD_HAS_NEVER_RAN() { + assertThat(job.getLastUnstableBuild()).isEqualTo(Build.BUILD_HAS_NEVER_RUN); } @Test - public void getLastSuccessfulBuildShouldNotBeNull() - throws IOException - { - assertThat( job.getLastSuccessfulBuild() ).isNotNull(); - checkJob( job.getLastSuccessfulBuild().details() ); + public void hasLastUnstableBuildShouldBeFalse() { + assertThat(job.hasLastUnstableBuildRun()).isFalse(); } @Test - public void getLastUnstableBuildShouldBeBUILD_HAS_NEVER_RAN() - { - assertThat( job.getLastUnstableBuild() ).isEqualTo(Build.BUILD_HAS_NEVER_RAN); + public void getLastUnsuccessfulBuildShouldBeBUILD_HAS_NEVER_RAN() { + assertThat(job.getLastUnsuccessfulBuild()).isEqualTo(Build.BUILD_HAS_NEVER_RUN); } @Test - public void getLastUnsuccessfulBuildShouldBeBUILD_HAS_NEVER_RAN() - { - assertThat( job.getLastUnsuccessfulBuild() ).isEqualTo(Build.BUILD_HAS_NEVER_RAN); + public void hasLastUnsuccessfulBuildShouldBeFalse() { + assertThat(job.hasLastUnsuccessfulBuildRun()).isFalse(); } + + @Test + public void getDescriptionShouldRetrunTheDescription() { + assertThat(job.getDescription()).isEqualTo("This is the description with umlauts äöü"); + } + } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java index 69b2f3f1..38a3507a 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobXmlIT.java @@ -7,58 +7,46 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import com.google.common.base.Joiner; - -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetJobXmlIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetJobXmlIT extends AbstractJenkinsIntegrationCase { private String jobXml; @BeforeMethod - public void beforeMethod() - throws IOException - { - jobXml = jenkinsServer.getJobXml( "test" ); + public void beforeMethod() throws IOException { + jobXml = jenkinsServer.getJobXml("test"); } - - private static final String[] CONFIG_XML = { - "", - "", + + //@formatter:off + private static final String[] CONFIG_XML = { + "", + "", " ", - " ", - " false", - " ", - " ", - " 0", - " 0", - " false", - " project", - " ", - " ", + " This is the description with umlauts äöü", + " false", + " ", " ", - " true", + " true", " false", " false", - " false", + " false", " ", - " false", - " ", + " false", + " ", " ", - " echo "test"", - " ", + " echo "test"", + " ", " ", - " ", - " ", - "" + " ", + " ", + "" }; + //@formatter:on @Test - public void getJobXmlShouldReturnTheExpectedConfigXml() - { - String expectedXml = Joiner.on( "\n" ).join( CONFIG_XML ); - assertThat( jobXml ).isEqualTo( expectedXml ); + public void getJobXmlShouldReturnTheExpectedConfigXml() { + String expectedXml = String.join("\n", CONFIG_XML); + assertThat(jobXml).isEqualTo(expectedXml); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobsIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobsIT.java index 652d6a9d..9e8fe10c 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobsIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetJobsIT.java @@ -10,31 +10,25 @@ import com.offbytwo.jenkins.model.Job; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetJobsIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetJobsIT extends AbstractJenkinsIntegrationCase { private Map jobs; @BeforeMethod - public void beforeMethod() - throws IOException - { + public void beforeMethod() throws IOException { jobs = jenkinsServer.getJobs(); } @Test - public void numberOfJobsIsEqualOne() - { - assertThat( jobs ).hasSize( 1 ); + public void numberOfJobsIsEqualOne() { + assertThat(jobs).hasSize(1); } @Test - public void getNameShouldReturnTest() - { + public void getNameShouldReturnTest() { String key = jobs.keySet().iterator().next(); - assertThat( jobs.get( key ).getName() ).isEqualTo( "test" ); + assertThat(jobs.get(key).getName()).isEqualTo("test"); } - + } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetOfflineCauseIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetOfflineCauseIT.java index 2700832c..321e7326 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetOfflineCauseIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetOfflineCauseIT.java @@ -9,41 +9,35 @@ import com.offbytwo.jenkins.model.OfflineCause; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedGetOfflineCauseIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetOfflineCauseIT extends AbstractJenkinsIntegrationCase { private OfflineCause offlineCause; @BeforeMethod - public void beforeMethod() - throws IOException - { - offlineCause = jenkinsServer.getComputerSet().getComputer().get( 0 ).getOfflineCause(); + public void beforeMethod() throws IOException { + offlineCause = jenkinsServer.getComputerSet().getComputers().get(0).getOfflineCause(); } /** - * This is a timestamp so I really can't make a test which compares to a real value. TODO: Think about this... + * This is a timestamp so I really can't make a test which compares to a + * real value. TODO: Think about this... */ @Test - public void getTimestampShouldReturnNonZero() - { + public void getTimestampShouldReturnNonZero() { // FIXME: This magic number is in the config.xml - // I need to find a simply way to read the config.xml and get the value from there. - assertThat( offlineCause.getTimestamp() ).isEqualTo( 1453986179962L ); + // I need to find a simply way to read the config.xml and get the value + // from there. + assertThat(offlineCause.getTimestamp()).isEqualTo(1453986179962L); } @Test - public void getOfflineCauseGetDescriptionShouldReturnDescription() - throws IOException - { - assertThat( offlineCause.getDescription() ).isEqualTo( "Disconnected by anonymous : Manually turned off" ); + public void getOfflineCauseGetDescriptionShouldReturnDescription() throws IOException { + assertThat(offlineCause.getDescription()).isEqualTo("Disconnected by anonymous : Manually turned off"); } @Test - public void getDescriptionShouldReturnTheAppropriateMessage() - { - assertThat( offlineCause.getDescription() ).isEqualTo( "Disconnected by anonymous : Manually turned off" ); + public void getDescriptionShouldReturnTheAppropriateMessage() { + assertThat(offlineCause.getDescription()).isEqualTo("Disconnected by anonymous : Manually turned off"); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetViewIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetViewIT.java new file mode 100644 index 00000000..7a6b71a9 --- /dev/null +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedGetViewIT.java @@ -0,0 +1,59 @@ +package com.offbytwo.jenkins.integration; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.offbytwo.jenkins.model.Job; +import com.offbytwo.jenkins.model.View; + +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedGetViewIT extends AbstractJenkinsIntegrationCase { + + private Map views; + + @BeforeMethod + public void beforeMethod() throws IOException { + views = jenkinsServer.getViews(); + } + + @Test + public void numberOfViewsIsEqualOne() { + assertThat(views).hasSize(2); + } + + @Test + public void viewNameShouldBeTestView() { + assertThat(views.containsKey("Test-View")).isTrue(); + assertThat(views.containsKey("All")).isTrue(); + } + + @Test + public void getJobsFromGetViews() throws IOException { + List jobs = views.get("Test-View").getJobs(); + assertThat(jobs).hasSize(1); + } + + @Test + public void getJobsViaView() throws IOException { + View view = jenkinsServer.getView("Test-View"); + List jobs = view.getJobs(); + assertThat(jobs).hasSize(1); + } + + @Test + public void getJobsViaViewWithDetails() throws IOException { + View view = jenkinsServer.getView("Test-View"); + List jobs = view.getJobs(); + assertThat(jobs).hasSize(1); + + Job job = jobs.get(0); + assertThat(job.getName()).isEqualTo("test"); + assertThat(job.details()).isNotNull(); + } +} diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedPluginManagerIT.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedPluginManagerIT.java index ffe7044d..bf39e06e 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedPluginManagerIT.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedPluginManagerIT.java @@ -9,136 +9,139 @@ import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; +import com.offbytwo.jenkins.helper.JenkinsVersion; import com.offbytwo.jenkins.model.Plugin; import com.offbytwo.jenkins.model.PluginManager; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedPluginManagerIT - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedPluginManagerIT extends AbstractJenkinsIntegrationCase { private PluginManager pluginManager; @BeforeMethod - public void beforeMethod() - throws IOException - { + public void beforeMethod() throws IOException { pluginManager = jenkinsServer.getPluginManager(); } @Test - public void getPluginsShouldReturn9ForJenkins20() - { - //TODO: Check why there is such a difference in the number of Plugins? - if (!jenkinsServer.getVersion().equals("2.0")) { - throw new SkipException("Not Version 2.0"); + public void getPluginsShouldReturn9ForJenkins20() { + // TODO: Check why there is such a difference in the number of Plugins? + if (jenkinsServer.getVersion().isLessThan("2.0")) { + throw new SkipException("Not Version 2.0 (" + jenkinsServer.getVersion() + ")"); } - assertThat( pluginManager.getPlugins() ).hasSize( 9 ); + assertThat(pluginManager.getPlugins()).hasSize(9); } + @Test - public void getPluginsShouldReturn26ForJenkins1651() - { - //TODO: Check why there is such a difference in the number of Plugins? - if (!jenkinsServer.getVersion().equals("1.651")) { - throw new SkipException("Not Version 1.651"); + public void getPluginsShouldReturn27ForJenkins1651() { + JenkinsVersion jv = jenkinsServer.getVersion(); + if (jv.isLessThan("1.651") && jv.isGreaterThan("1.651.3")) { + throw new SkipException("Not Version 1.651 (" + jv.toString() + ")"); } - assertThat( pluginManager.getPlugins() ).hasSize( 26 ); + assertThat(pluginManager.getPlugins()).hasSize(27); } private Plugin createPlugin(String shortName, String version) { Plugin result = new Plugin(); - result.setShortName( shortName ); - result.setVersion( version ); + result.setShortName(shortName); + result.setVersion(version); return result; - } - + } + @Test public void getPluginsShouldReturnTheListOfInstalledPluginsForJenkins20() { - - if (!jenkinsServer.getVersion().equals("2.0")) { - throw new SkipException("Not Version 2.0"); + + if (jenkinsServer.getVersion().isLessThan("2.0")) { + throw new SkipException("Not Version 2.0 (" + jenkinsServer.getVersion() + ")"); } - - //TODO: The list of plugins is contained in the plugin.txt + + // TODO: The list of plugins is contained in the plugin.txt // which should be read and used as base of comparison. // instead of maintaining at two locations. - Plugin[] expectedPlugins = { - createPlugin("token-macro", "1.12.1"), + //@formatter:off + Plugin[] expectedPlugins = { + createPlugin("token-macro", "1.12.1"), createPlugin("testng-plugin", "1.10"), - createPlugin("job-dsl", "1.41"), - createPlugin("junit", "1.10"), + createPlugin("job-dsl", "1.41"), + createPlugin("junit", "1.10"), createPlugin("jacoco", "1.0.19"), - createPlugin("config-file-provider", "2.10.0"), + createPlugin("config-file-provider", "2.10.0"), createPlugin("timestamper", "1.7.2"), - createPlugin("credentials", "1.24"), - createPlugin("throttle-concurrents", "1.8.4"), + createPlugin("credentials", "1.24"), + createPlugin("throttle-concurrents", "1.9.0"), + createPlugin("cloudbees-folder", "5.12"), }; + //@formatter:on List plugins = pluginManager.getPlugins(); - - for ( Plugin plugin : plugins ) - { + + for (Plugin plugin : plugins) { boolean found = false; - for ( int i = 0; i < expectedPlugins.length; i++ ) - { - if (plugin.getShortName().equals( expectedPlugins[i].getShortName()) && - plugin.getVersion().equals( expectedPlugins[i].getVersion())) { + for (int i = 0; i < expectedPlugins.length; i++) { + if (plugin.getShortName().equals(expectedPlugins[i].getShortName()) + && plugin.getVersion().equals(expectedPlugins[i].getVersion())) { found = true; } } - assertThat( found ).isTrue().as("Plugin shortName:{} version:{} couldn't be found.", plugin.getShortName(), plugin.getVersion()); + assertThat(found).isTrue().as("Plugin shortName:{} version:{} couldn't be found.", plugin.getShortName(), + plugin.getVersion()); } - + } + @Test - public void getPluginsShouldReturnTheListOfInstalledPluginsFor1651() - { - if (!jenkinsServer.getVersion().equals("1.651")) { - throw new SkipException("Not Version 1.651"); + public void getPluginsShouldReturnTheListOfInstalledPluginsFor1651() { + JenkinsVersion jv = jenkinsServer.getVersion(); + if (jv.isLessThan("1.651") && jv.isGreaterThan("1.651.3")) { + throw new SkipException("Not Version 1.651 (" + jv + ")"); } - //TODO: The list of plugins is contained in the plugin.txt + + // TODO: Check why there is such a difference in the number of Plugins? + // TODO: The list of plugins is contained in the plugin.txt // which should be read and used as base of comparison. // instead of maintaining at two locations. - Plugin[] expectedPlugins = { - createPlugin("token-macro", "1.12.1"), + //@formatter:off + Plugin[] expectedPlugins = { + createPlugin("token-macro", "1.12.1"), createPlugin("translation", "1.10"), - createPlugin("testng-plugin", "1.10"), + createPlugin("testng-plugin", "1.10"), createPlugin("matrix-project", "1.4.1"), - createPlugin("job-dsl", "1.41"), + createPlugin("job-dsl", "1.41"), createPlugin("windows-slaves", "1.0"), - createPlugin("antisamy-markup-formatter", "1.1"), + createPlugin("antisamy-markup-formatter", "1.1"), createPlugin("junit", "1.10"), - createPlugin("maven-plugin", "2.7.1"), + createPlugin("maven-plugin", "2.7.1"), createPlugin("external-monitor-job", "1.4"), - createPlugin("jacoco", "1.0.19"), - createPlugin("pam-auth", "1.1"), + createPlugin("jacoco", "1.0.19"), + createPlugin("pam-auth", "1.1"), createPlugin("ldap", "1.11"), - createPlugin("script-security", "1.13"), - createPlugin("mailer", "1.11"), + createPlugin("script-security", "1.13"), + createPlugin("mailer", "1.11"), createPlugin("cvs", "2.11"), - createPlugin("ant", "1.2"), + createPlugin("ant", "1.2"), createPlugin("config-file-provider", "2.10.0"), - createPlugin("ssh-credentials", "1.10"), + createPlugin("ssh-credentials", "1.10"), createPlugin("matrix-auth", "1.1"), - createPlugin("javadoc", "1.1"), + createPlugin("javadoc", "1.1"), createPlugin("timestamper", "1.7.2"), - createPlugin("credentials", "1.24"), - createPlugin("throttle-concurrents", "1.8.4"), - createPlugin("subversion", "1.54"), + createPlugin("credentials", "1.24"), + createPlugin("throttle-concurrents", "1.9.0"), + createPlugin("subversion", "1.54"), createPlugin("ssh-slaves", "1.9"), + createPlugin("cloudbees-folder", "5.12"), }; + //@formatter:on List plugins = pluginManager.getPlugins(); - - for ( Plugin plugin : plugins ) - { + + for (Plugin plugin : plugins) { boolean found = false; - for ( int i = 0; i < expectedPlugins.length; i++ ) - { - if (plugin.getShortName().equals( expectedPlugins[i].getShortName()) && - plugin.getVersion().equals( expectedPlugins[i].getVersion())) { + for (int i = 0; i < expectedPlugins.length; i++) { + if (plugin.getShortName().equals(expectedPlugins[i].getShortName()) + && plugin.getVersion().equals(expectedPlugins[i].getVersion())) { found = true; } } - assertThat( found ).isTrue().as("Plugin shortName:{} version:{} couldn't be found.", plugin.getShortName(), plugin.getVersion()); + assertThat(found).isTrue().as("Plugin shortName:{} version:{} couldn't be found.", plugin.getShortName(), + plugin.getVersion()); } } diff --git a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedTrigger.java b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedTrigger.java index 215dce0b..d6f91b49 100644 --- a/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedTrigger.java +++ b/jenkins-client-it-docker/src/test/java/com/offbytwo/jenkins/integration/NoExecutorStartedTrigger.java @@ -7,21 +7,16 @@ import com.offbytwo.jenkins.model.Job; -@Test( groups = { Groups.NO_EXECUTOR_GROUP } ) -public class NoExecutorStartedTrigger - extends AbstractJenkinsIntegrationCase -{ +@Test(groups = { Groups.NO_EXECUTOR_GROUP }) +public class NoExecutorStartedTrigger extends AbstractJenkinsIntegrationCase { private Job pluginManager; @BeforeMethod - public void beforeMethod() - throws IOException - { - pluginManager = jenkinsServer.getJob( "test" ); + public void beforeMethod() throws IOException { + pluginManager = jenkinsServer.getJob("test"); } - @Test public void buildWillStartABuildButWillNotBeingExecuted() throws IOException { pluginManager.build(); diff --git a/jenkins-client/pom.xml b/jenkins-client/pom.xml index f85509f9..2cbc3df9 100644 --- a/jenkins-client/pom.xml +++ b/jenkins-client/pom.xml @@ -11,7 +11,7 @@ com.offbytwo.jenkins jenkins-client-parent - 0.3.6-SNAPSHOT + 0.4.0-SNAPSHOT jenkins-client @@ -34,7 +34,7 @@ - dom4j + org.dom4j dom4j @@ -52,13 +52,8 @@ - com.google.guava - guava - - - - commons-lang - commons-lang + org.apache.commons + commons-lang3 @@ -71,6 +66,16 @@ httpclient + + org.apache.httpcomponents + httpcore + + + + org.apache.httpcomponents + httpmime + + jaxen jaxen @@ -104,10 +109,9 @@ assertj-core test - - xml-apis - xml-apis + xerces + xmlParserAPIs @@ -118,6 +122,7 @@ maven-shade-plugin + httpclient package shade @@ -125,17 +130,12 @@ - com.google.guava:guava + org.apache.httpcomponents:httpclient + org.apache.httpcomponents:httpcore true - stash - - - com.google.common - com.google.common.jenkins_client_jarjar - - + apachehttp diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java index d79d4a80..01755baa 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsServer.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.net.URI; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -20,12 +21,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; import com.offbytwo.jenkins.client.JenkinsHttpClient; +import com.offbytwo.jenkins.client.JenkinsHttpConnection; import com.offbytwo.jenkins.client.util.EncodingUtils; +import com.offbytwo.jenkins.client.util.UrlUtils; +import com.offbytwo.jenkins.helper.JenkinsVersion; import com.offbytwo.jenkins.model.Build; import com.offbytwo.jenkins.model.Computer; import com.offbytwo.jenkins.model.ComputerSet; @@ -41,20 +41,30 @@ import com.offbytwo.jenkins.model.QueueItem; import com.offbytwo.jenkins.model.QueueReference; import com.offbytwo.jenkins.model.View; +import java.io.Closeable; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.offbytwo.jenkins.helper.FunctionalHelper.SET_CLIENT; +import static java.util.stream.Collectors.toMap; /** * The main starting point for interacting with a Jenkins server. */ -public class JenkinsServer { - private final Logger LOGGER = LoggerFactory.getLogger( getClass() ); +public class JenkinsServer implements Closeable { + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); - private final JenkinsHttpClient client; + /** + * The transport client instance to use. + */ + private final JenkinsHttpConnection client; /** * Create a new Jenkins server reference given only the server address * - * @param serverUri - * address of jenkins server (ex. http://localhost:8080/jenkins) + * @param serverUri address of jenkins server (ex. + * http://localhost:8080/jenkins) */ public JenkinsServer(URI serverUri) { this(new JenkinsHttpClient(serverUri)); @@ -63,12 +73,10 @@ public JenkinsServer(URI serverUri) { /** * Create a new Jenkins server reference given the address and credentials * - * @param serverUri - * address of jenkins server (ex. http://localhost:8080/jenkins) - * @param username - * username to use when connecting - * @param passwordOrToken - * password (not recommended) or token (recommended) + * @param serverUri address of jenkins server (ex. + * http://localhost:8080/jenkins) + * @param username username to use when connecting + * @param passwordOrToken password (not recommended) or token (recommended) */ public JenkinsServer(URI serverUri, String username, String passwordOrToken) { this(new JenkinsHttpClient(serverUri, username, passwordOrToken)); @@ -77,10 +85,9 @@ public JenkinsServer(URI serverUri, String username, String passwordOrToken) { /** * Create a new Jenkins server directly from an HTTP client (ADVANCED) * - * @param client - * Specialized client to use. + * @param client Specialized client to use. */ - public JenkinsServer(JenkinsHttpClient client) { + public JenkinsServer(final JenkinsHttpConnection client) { this.client = client; } @@ -100,22 +107,24 @@ public boolean isRunning() { } /** - * @return The Jenkins version. + * @return {@link JenkinsVersion} */ - public String getVersion() { - if (client.getJenkinsVersion().isEmpty()) { - //Force a request to get at least once - //HttpHeader + public JenkinsVersion getVersion() { + if (!client.isJenkinsVersionSet()) { + // Force a request to get at least once + // HttpHeader. The header contains the version + // information. isRunning(); } - return client.getJenkinsVersion(); + JenkinsVersion jv = new JenkinsVersion(client.getJenkinsVersion()); + return jv; } /** * Get a list of all the defined jobs on the server (at the summary level) * * @return list of defined jobs (summary level, for details @see Job#details - * @throws IOException + * @throws IOException in case of an error. */ public Map getJobs() throws IOException { return getJobs(null, null); @@ -124,8 +133,9 @@ public Map getJobs() throws IOException { /** * Get a list of all the defined jobs on the server (in the given folder) * + * @param folder {@link FolderJob} * @return list of defined jobs (summary level, for details @see Job#details - * @throws IOException + * @throws IOException in case of an error. */ public Map getJobs(FolderJob folder) throws IOException { return getJobs(folder, null); @@ -135,8 +145,9 @@ public Map getJobs(FolderJob folder) throws IOException { * Get a list of all the defined jobs on the server (at the specified view * level) * + * @param view The view to get jobs from. * @return list of defined jobs (view level, for details @see Job#details - * @throws IOException + * @throws IOException in case of an error. */ public Map getJobs(String view) throws IOException { return getJobs(null, view); @@ -145,35 +156,31 @@ public Map getJobs(String view) throws IOException { /** * Get a list of all the defined jobs on the server (at the specified view * level and in the specified folder) - * + * + * @param folder {@link FolderJob} + * @param view The view to use. * @return list of defined jobs (view level, for details @see Job#details - * @throws IOException + * @throws IOException in case of an error. */ public Map getJobs(FolderJob folder, String view) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); - } + String path = UrlUtils.toBaseUrl(folder); Class viewClass = MainView.class; if (view != null) { path = path + "view/" + EncodingUtils.encode(view) + "/"; viewClass = View.class; } List jobs = client.get(path, viewClass).getJobs(); - return Maps.uniqueIndex(jobs, new Function() { - @Override - public String apply(Job job) { - job.setClient(client); - return job.getName(); - } - }); + + return jobs.stream() + .map(SET_CLIENT(this.client)) + .collect(toMap(s -> s.getName(), s -> s)); } /** * Get a list of all the defined views on the server (at the summary level) * * @return list of defined views - * @throws IOException + * @throws IOException in case of an error. */ public Map getViews() throws IOException { return getViews(null); @@ -182,33 +189,40 @@ public Map getViews() throws IOException { /** * Get a list of all the defined views on the server (at the summary level * and in the given folder) - * + * + * @param folder {@link FolderJob} * @return list of defined views - * @throws IOException + * @throws IOException in case of an error. */ public Map getViews(FolderJob folder) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); - } - List views = client.get(path, MainView.class).getViews(); - return Maps.uniqueIndex(views, new Function() { - @Override - public String apply(View view) { - view.setClient(client); - // return view.getName().toLowerCase(); - return view.getName(); - } - }); + // This is much better than using &depth=2 + // http://localhost:8080/api/json?pretty&tree=views[name,url,jobs[name,url]] + List views = client.get(UrlUtils.toBaseUrl(folder) + "?tree=views[name,url,jobs[name,url]]", MainView.class).getViews(); + + //TODO: Think about this Lambda. It's too large? Make it smaller! + return views.stream().map(view -> { + SET_CLIENT(this.client); + + // TODO: Think about the following? Does there exists a simpler/more + // elegant method? + for (Job job : view.getJobs()) { + SET_CLIENT(this.client); + } + for (View viewView : view.getViews()) { + SET_CLIENT(this.client); + } + + return view; + }) + .collect(Collectors.toMap(s -> s.getName(), v -> v)); } /** * Get a single view object from the server * - * @param name - * name of the view in Jenkins + * @param name name of the view in Jenkins * @return the view object - * @throws IOException + * @throws IOException in case of an error. */ public View getView(String name) throws IOException { return getView(null, name); @@ -216,49 +230,65 @@ public View getView(String name) throws IOException { /** * Get a single view object from the given folder - * - * @param name - * name of the view in Jenkins + * + * @param folder The name of the folder. + * @param name name of the view in Jenkins * @return the view object - * @throws IOException + * @throws IOException in case of an error. */ public View getView(FolderJob folder, String name) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); + try { + View resultView = client.get(UrlUtils.toViewBaseUrl(folder, name) + "/", View.class); + resultView.setClient(client); + + // TODO: Think about the following? Does there exists a simpler/more + // elegant method? + for (Job job : resultView.getJobs()) { + job.setClient(client); + } + for (View view : resultView.getViews()) { + view.setClient(client); + } + return resultView; + } catch (HttpResponseException e) { + LOGGER.debug("getView(folder={}, name={}) status={}", folder, name, e.getStatusCode()); + if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + // TODO: Think hard about this. + return null; + } + throw e; } - return client.get(path + "view/" + EncodingUtils.encode(name) + "/", View.class); } /** * Get a single Job from the server. - * + * + * @param jobName name of the job to get details of. * @return A single Job, null if not present - * @throws IOException + * @throws IOException in case of an error. */ public JobWithDetails getJob(String jobName) throws IOException { - return getJob(null, jobName); + return getJob(null, UrlUtils.toFullJobPath(jobName)); } /** * Get a single Job from the given folder. - * + * + * @param folder {@link FolderJob} + * @param jobName name of the job to get details of. * @return A single Job, null if not present - * @throws IOException + * @throws IOException in case of an error. */ public JobWithDetails getJob(FolderJob folder, String jobName) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); - } try { - JobWithDetails job = client.get(path + "job/" + EncodingUtils.encode(jobName), JobWithDetails.class); + JobWithDetails job = client.get(UrlUtils.toJobBaseUrl(folder, jobName), JobWithDetails.class); job.setClient(client); return job; } catch (HttpResponseException e) { LOGGER.debug("getJob(folder={}, jobName={}) status={}", folder, jobName, e.getStatusCode()); if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + // TODO: Think hard about this. return null; } throw e; @@ -266,17 +296,12 @@ public JobWithDetails getJob(FolderJob folder, String jobName) throws IOExceptio } public MavenJobWithDetails getMavenJob(String jobName) throws IOException { - return getMavenJob(null, jobName); + return getMavenJob(null, UrlUtils.toFullJobPath(jobName)); } public MavenJobWithDetails getMavenJob(FolderJob folder, String jobName) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); - } try { - MavenJobWithDetails job = client.get(path + "job/" + EncodingUtils.encode(jobName), - MavenJobWithDetails.class); + MavenJobWithDetails job = client.get(UrlUtils.toJobBaseUrl(folder, jobName), MavenJobWithDetails.class); job.setClient(client); return job; @@ -293,14 +318,15 @@ public Optional getFolderJob(Job job) throws IOException { try { FolderJob folder = client.get(job.getUrl(), FolderJob.class); if (!folder.isFolder()) { - return Optional.absent(); + return Optional.empty(); } folder.setClient(client); - return Optional.of(folder); } catch (HttpResponseException e) { + LOGGER.debug("getForlderJob(job={}) status={}", job, e.getStatusCode()); if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { - //TODO: Check if this is a good idea ? What about Optional.absent() ? + // TODO: Check if this is a good idea ? What about + // Optional.absent() ? return null; } throw e; @@ -309,109 +335,201 @@ public Optional getFolderJob(Job job) throws IOException { /** * Create a job on the server using the provided xml - * - * @return the new job object - * @throws IOException + * + * @param jobName name of the job to be created. + * @param jobXml the config.xml which should be used to create + * the job. + * @throws IOException in case of an error. */ - public void createJob(String jobName, String jobXml) throws IOException { - createJob(null, jobName, jobXml, true); + public JenkinsServer createJob(String jobName, String jobXml) throws IOException { + return createJob(null, jobName, jobXml, false); } /** * Create a job on the server using the provided xml - * - * @return the new job object - * @throws IOException + * + * @param jobName name of the job to be created. + * @param jobXml the config.xml which should be used to create + * the job. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException in case of an error. */ - public void createJob(String jobName, String jobXml, Boolean crumbFlag) throws IOException { - createJob(null, jobName, jobXml, crumbFlag); + public JenkinsServer createJob(String jobName, String jobXml, Boolean crumbFlag) throws IOException { + return createJob(null, jobName, jobXml, crumbFlag); } /** * Create a job on the server using the provided xml and in the provided * folder * - * @return the new job object - * @throws IOException + * @param folder {@link FolderJob} + * @param jobName name of the job to be created. + * @param jobXml the config.xml which should be used to create + * the job. + * @throws IOException in case of an error. */ - public void createJob(FolderJob folder, String jobName, String jobXml) throws IOException { - createJob(folder, jobName, jobXml, true); + public JenkinsServer createJob(FolderJob folder, String jobName, String jobXml) throws IOException { + return createJob(folder, jobName, jobXml, false); } /** * Create a job on the server using the provided xml and in the provided * folder * - * @return the new job object - * @throws IOException + * @param folder {@link FolderJob} + * @param jobName name of the job to be created. + * @param jobXml the config.xml which should be used to create + * the job. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException in case of an error. */ - public void createJob(FolderJob folder, String jobName, String jobXml, Boolean crumbFlag) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); - } - client.post_xml(path + "createItem?name=" + EncodingUtils.encodeParam(jobName), jobXml, crumbFlag); + public JenkinsServer createJob(FolderJob folder, String jobName, String jobXml, Boolean crumbFlag) + throws IOException { + client.post_xml(UrlUtils.toBaseUrl(folder) + "createItem?name=" + EncodingUtils.formParameter(jobName), jobXml, crumbFlag); + return this; } /** - * Create a folder on the server (in the root) + * Create a view on the server using the provided xml + * + * @param viewName name of the view to be created. + * @param viewXml The configuration for the view. + * @throws IOException in case of an error. + */ + public JenkinsServer createView(String viewName, String viewXml) throws IOException { + return createView(null, viewName, viewXml, false); + } + + /** + * Create a view on the server using the provided xml. * - * @throws IOException + * @param viewName name of the view to be created. + * @param viewXml The configuration for the view. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException in case of an error. + */ + public JenkinsServer createView(String viewName, String viewXml, Boolean crumbFlag) throws IOException { + return createView(null, viewName, viewXml, crumbFlag); + } + + /** + * Create a view on the server using the provided xml and in the provided + * folder. + * + * @param folder {@link FolderJob} + * @param viewName name of the view to be created. + * @param viewXml The configuration for the view. + * @throws IOException in case of an error. */ - public void createFolder(String jobName) throws IOException { - createFolder(null, jobName, false); + public JenkinsServer createView(FolderJob folder, String viewName, String viewXml) throws IOException { + return createView(folder, viewName, viewXml, false); + } + + /** + * Create a view on the server using the provided xml and in the provided + * folder. + * + * @param folder the folder. + * @param viewName the view name. + * @param viewXml the view xml. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException in case of an error. + */ + public JenkinsServer createView(FolderJob folder, String viewName, String viewXml, Boolean crumbFlag) + throws IOException { + client.post_xml(UrlUtils.toBaseUrl(folder) + "createView?name=" + EncodingUtils.formParameter(viewName), viewXml, + crumbFlag); + return this; } /** * Create a folder on the server (in the root) - * - * @throws IOException + * + * @param folderName name of the folder. + * @throws IOException in case of an error. */ - public void createFolder(String jobName, Boolean crumbFlag) throws IOException { - createFolder(null, jobName, crumbFlag); + public JenkinsServer createFolder(String folderName) throws IOException { + return createFolder(null, folderName, false); } /** - * Create a folder on the server (in the given folder) + * Create a folder on the server (in the root) + * + * @param folderName name of the folder. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException in case of an error. + */ + public JenkinsServer createFolder(String folderName, Boolean crumbFlag) throws IOException { + return createFolder(null, folderName, crumbFlag); + } + + /** + * Create a job on the server (in the given folder) * - * @throws IOException + * @param folder {@link FolderJob} + * @param jobName name of the job. + * @throws IOException in case of an error. */ - public void createFolder(FolderJob folder, String jobName) throws IOException { - createFolder(folder, jobName, false); + public JenkinsServer createFolder(FolderJob folder, String jobName) throws IOException { + return createFolder(folder, jobName, false); } /** - * Create a folder on the server (in the given folder) + * Create a job on the server (in the given folder) * - * @throws IOException + * @param folder {@link FolderJob} + * @param jobName name of the job. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException in case of an error. */ - public void createFolder(FolderJob folder, String jobName, Boolean crumbFlag) throws IOException { - String path = "/"; - if (folder != null) { - path = folder.getUrl(); - } + public JenkinsServer createFolder(FolderJob folder, String jobName, Boolean crumbFlag) throws IOException { // https://gist.github.com/stuart-warren/7786892 was slightly helpful // here - ImmutableMap params = ImmutableMap.of("mode", "com.cloudbees.hudson.plugins.folder.Folder", - "name", EncodingUtils.encodeParam(jobName), "from", "", "Submit", "OK"); - client.post_form(path + "createItem?", params, crumbFlag); + //TODO: JDK9+: Map.of(...) + Map params = new HashMap<>(); + params.put("mode", "com.cloudbees.hudson.plugins.folder.Folder"); + params.put("name", jobName); + params.put("from", ""); + params.put("Submit", "OK"); + client.post_form(UrlUtils.toBaseUrl(folder) + "createItem?", params, crumbFlag); + return this; } /** * Get the xml description of an existing job * + * @param jobName name of the job. * @return the new job object - * @throws IOException + * @throws IOException in case of an error. */ public String getJobXml(String jobName) throws IOException { - return client.get("/job/" + EncodingUtils.encode(jobName) + "/config.xml"); + return getJobXml(null, jobName); + } + + /** + * Get the xml description of an existing job. + * + * @param jobName name of the job. + * @param folder {@link FolderJob} + * @return the new job object + * @throws IOException in case of an error. + */ + public String getJobXml(FolderJob folder, String jobName) throws IOException { + return client.get(UrlUtils.toJobBaseUrl(folder, jobName) + "/config.xml"); } /** * Get the description of an existing Label - * - * @return label object - * @throws IOException + * + * @param labelName name of the label. + * @return {@link LabelWithDetails} + * @throws IOException in case of an error. */ public LabelWithDetails getLabel(String labelName) throws IOException { return client.get("/label/" + EncodingUtils.encode(labelName), LabelWithDetails.class); @@ -420,19 +538,15 @@ public LabelWithDetails getLabel(String labelName) throws IOException { /** * Get a list of all the computers on the server (at the summary level) * - * @return list of defined computers (summary level, for details @see + * @return map of defined computers (summary level, for details @see * Computer#details - * @throws IOException + * @throws IOException in case of an error. */ public Map getComputers() throws IOException { List computers = client.get("computer/", Computer.class).getComputers(); - return Maps.uniqueIndex(computers, new Function() { - @Override - public String apply(Computer computer) { - computer.setClient(client); - return computer.getDisplayName().toLowerCase(); - } - }); + return computers.stream() + .map(SET_CLIENT(this.client)) + .collect(Collectors.toMap(s -> s.getDisplayName().toLowerCase(), Function.identity())); } /** @@ -441,137 +555,224 @@ public String apply(Computer computer) { * {@link ComputerSet#getTotalExecutors()}. * * @return {@link ComputerSet} - * @throws IOException + * @throws IOException in case of an error. */ public ComputerSet getComputerSet() throws IOException { return client.get("computer/?depth=2", ComputerSet.class); } - + /** * This will give you back the {@link PluginManager}. + * * @return {@link PluginManager} * @throws IOException in case of a failure. */ public PluginManager getPluginManager() throws IOException { - return client.get( "pluginManager/?depth=2", PluginManager.class ); + return client.get("pluginManager/?depth=2", PluginManager.class); + } + + /** + * Update the xml description of an existing view + * + * @param viewName name of the view. + * @param viewXml the view configuration. + * @throws IOException in case of an error. + */ + public JenkinsServer updateView(String viewName, String viewXml) throws IOException { + return this.updateView(viewName, viewXml, true); + } + + public JenkinsServer updateView(String viewName, String viewXml, boolean crumbFlag) throws IOException { + client.post_xml("/view/" + EncodingUtils.encode(viewName) + "/config.xml", viewXml, crumbFlag); + return this; + } + + public JenkinsServer updateView(FolderJob folder, String viewName, String viewXml) throws IOException { + client.post_xml(UrlUtils.toBaseUrl(folder) + "view/" + EncodingUtils.encode(viewName) + "/config.xml", viewXml, true); + return this; + } + + public JenkinsServer updateView(FolderJob folder, String viewName, String viewXml, boolean crumbFlag) + throws IOException { + client.post_xml(UrlUtils.toBaseUrl(folder) + "view/" + EncodingUtils.encode(viewName) + "/config.xml", viewXml, crumbFlag); + return this; } /** * Update the xml description of an existing job * - * @return the new job object - * @throws IOException + * @param jobName name of the job to be updated. + * @param jobXml the configuration to be used for updating. + * @throws IOException in case of an error. */ - public void updateJob(String jobName, String jobXml) throws IOException { - this.updateJob(jobName, jobXml, true); + public JenkinsServer updateJob(String jobName, String jobXml) throws IOException { + return this.updateJob(jobName, jobXml, true); } - public void updateJob(String jobName, String jobXml, boolean crumbFlag) throws IOException { - client.post_xml("/job/" + EncodingUtils.encode(jobName) + "/config.xml", jobXml, crumbFlag); + /** + * Update the xml description of an existing job + * + * @param jobName name of the job to be updated. + * @param jobXml the configuration to be used for updating. + * @param crumbFlag true/false. + * @throws IOException in case of an error. + */ + public JenkinsServer updateJob(String jobName, String jobXml, boolean crumbFlag) throws IOException { + return updateJob(null, jobName, jobXml, crumbFlag); } - public void addStringParam(String jobName, String name, String description, String defaultValue) + /** + * Update the xml description of an existing job + * + * @param folder the folder. + * @param jobName the name of the job. + * @param jobXml the job xml configuration. + * @param crumbFlag true/false. + * @throws IOException in case of an error. + */ + public JenkinsServer updateJob(FolderJob folder, String jobName, String jobXml, boolean crumbFlag) + throws IOException { + client.post_xml(UrlUtils.toJobBaseUrl(folder, jobName) + "/config.xml", jobXml, crumbFlag); + return this; + } + + /** + * @param jobName name of the job. + * @param name name of the formParameter. + * @param description of the parameters. + * @param defaultValue the defaultValue for the parameters. + * @throws IOException in case of an error. + * @throws JAXBException in case of an error. + * @throws DocumentException in case of an error. + */ + public JenkinsServer addStringParam(String jobName, String name, String description, String defaultValue) throws IOException, JAXBException, DocumentException { String jobXml = this.getJobXml(jobName); JobConfiguration jobConf = new JobConfiguration(jobXml); jobXml = jobConf.addStringParam(name, description, defaultValue).asXml(); - this.updateJob(jobName, jobXml); + return this.updateJob(jobName, jobXml); } /** * Sends the Quiet Down (Prepare for shutdown) message * - * @throws IOException + * @throws IOException in case of an error. */ - public void quietDown() throws IOException { + public JenkinsServer quietDown() throws IOException { try { - client.get("/quietDown/"); + client.post("/quietDown/"); } catch (org.apache.http.client.ClientProtocolException e) { LOGGER.error("quietDown()", e); } - + return this; } /** * Cancels the Quiet Down (Prepare for shutdown) message * - * @throws IOException + * @throws IOException in case of an error. */ - public void cancelQuietDown() throws IOException { + public JenkinsServer cancelQuietDown() throws IOException { try { client.post("/cancelQuietDown/"); } catch (org.apache.http.client.ClientProtocolException e) { LOGGER.error("cancelQuietDown()", e); } + return this; } - /* - * Delete a job from jenkins + /** + * Delete a job from Jenkins within a folder. + * + * @param folder The folder where the given job is located. + * @param jobName The job which should be deleted. * - * @throws IOException + * @throws IOException in case of an error. */ - public void deleteJob(String jobName) throws IOException { - client.post("/job/" + EncodingUtils.encode(jobName) + "/doDelete"); + public JenkinsServer deleteJob(FolderJob folder, String jobName) throws IOException { + return deleteJob(folder, jobName, false); + } + + /** + * Delete a job from Jenkins within a folder. + * + * @param folder The folder where the given job is located. + * @param jobName The job which should be deleted. + * @param crumbFlag The crumbFlag + * @throws IOException in case of problems. + */ + public JenkinsServer deleteJob(FolderJob folder, String jobName, boolean crumbFlag) throws IOException { + client.post(UrlUtils.toJobBaseUrl(folder, jobName) + "/doDelete", crumbFlag); + return this; + } + + /** + * Delete a job from jenkins + * + * @param jobName The name of the job which should be deleted. + * @throws IOException in case of an error. + */ + public JenkinsServer deleteJob(String jobName) throws IOException { + return deleteJob(jobName, false); } /** * Delete a job from Jenkins. * - * @param jobName - * The name of the job to be deleted. - * @param crumbFlag - * The crumFlag. - * @throws IOException - * In case of an failure. - */ - public void deleteJob(String jobName, boolean crumbFlag) throws IOException { + * @param jobName The name of the job to be deleted. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException In case of an failure. + */ + public JenkinsServer deleteJob(String jobName, boolean crumbFlag) throws IOException { client.post("/job/" + EncodingUtils.encode(jobName) + "/doDelete", crumbFlag); + return this; } - /* + /** * Disable a job from jenkins - * - * @throws IOException + * + * @param jobName The name of the job which should be disabled. + * @throws IOException in case of an error. */ - public void disableJob(String jobName) throws IOException { - client.post("/job/" + EncodingUtils.encode(jobName) + "/disable"); + public JenkinsServer disableJob(String jobName) throws IOException { + return disableJob(jobName, false); } /** * Disable a job from Jenkins. * - * @param jobName - * The name of the job to be deleted. - * @param crumbFlag - * The crumFlag. - * @throws IOException - * In case of an failure. + * @param jobName The name of the job to be deleted. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException In case of an failure. */ - public void disableJob(String jobName, boolean crumbFlag) throws IOException { + public JenkinsServer disableJob(String jobName, boolean crumbFlag) throws IOException { client.post("/job/" + EncodingUtils.encode(jobName) + "/disable", crumbFlag); + return this; } - /* + /** * Enable a job from jenkins - * - * @throws IOException + * + * @param jobName name of the job which should be enabled. + * @throws IOException In case of an failure. */ - public void enableJob(String jobName) throws IOException { - client.post("/job/" + EncodingUtils.encode(jobName) + "/enable"); + public JenkinsServer enableJob(String jobName) throws IOException { + return enableJob(jobName, false); } /** * Enable a job from Jenkins. * - * @param jobName - * The name of the job to be deleted. - * @param crumbFlag - * The crumFlag. - * @throws IOException - * In case of an failure. + * @param jobName The name of the job to be deleted. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException In case of an failure. */ - public void enableJob(String jobName, boolean crumbFlag) throws IOException { + public JenkinsServer enableJob(String jobName, boolean crumbFlag) throws IOException { client.post("/job/" + EncodingUtils.encode(jobName) + "/enable", crumbFlag); + return this; } /** @@ -584,15 +785,36 @@ public void enableJob(String jobName, boolean crumbFlag) throws IOException { * recommended to use heuristics to check your return string for stack * traces by detecting strings like "groovy.lang.(something)Exception". * - * @param script - * @return results - * @throws IOException + * @param script The script to be executed. + * @return results The results of the script. + * @throws IOException in case of an error. */ public String runScript(String script) throws IOException { - return client.post_text("/scriptText", "script=" + script, ContentType.APPLICATION_FORM_URLENCODED, false); + return runScript(script, false); + } + + /** + * Runs the provided groovy script on the server and returns the result. + * + * This is similar to running groovy scripts using the script console. + * + * In the instance where your script causes an exception, the server still + * returns a 200 status, so detecting errors is very challenging. It is + * recommended to use heuristics to check your return string for stack + * traces by detecting strings like "groovy.lang.(something)Exception". + * + * @param script The script to run. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @return results The results of the run of the script. + * @throws IOException in case of an error. + */ + public String runScript(String script, boolean crumbFlag) throws IOException { + return client.post_text("/scriptText", "script=" + script, ContentType.APPLICATION_FORM_URLENCODED, crumbFlag); } public Queue getQueue() throws IOException { + // TODO: Check if using depth=1 is a good idea? return client.get("queue/?depth=1", Queue.class); } @@ -600,10 +822,10 @@ public QueueItem getQueueItem(QueueReference ref) throws IOException { try { String url = ref.getQueueItemUrlPart(); // "/queue/item/" + id - QueueItem job = client.get(url, QueueItem.class); - job.setClient(client); + QueueItem queueItem = client.get(url, QueueItem.class); + queueItem.setClient(client); - return job; + return queueItem; } catch (HttpResponseException e) { if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { return null; @@ -628,16 +850,146 @@ public Build getBuild(QueueItem q) throws IOException { throw e; } } + + /** + * Rename a job + * + * @param oldJobName existing job name. + * @param newJobName The new job name. + * @throws IOException In case of a failure. + */ + public JenkinsServer renameJob(String oldJobName, String newJobName) throws IOException { + return renameJob(null, oldJobName, newJobName, false); + } + + /** + * Rename a job + * + * @param oldJobName existing job name. + * @param newJobName The new job name. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException In case of a failure. + */ + public JenkinsServer renameJob(String oldJobName, String newJobName, Boolean crumbFlag) throws IOException { + renameJob(null, oldJobName, newJobName, crumbFlag); + return this; + } + + /** + * Rename a job + * + * @param folder {@link FolderJob} + * @param oldJobName existing job name. + * @param newJobName The new job name. + * @throws IOException In case of a failure. + */ + public JenkinsServer renameJob(FolderJob folder, String oldJobName, String newJobName) throws IOException { + return renameJob(folder, oldJobName, newJobName, false); + } + + /** + * Rename a job + * + * @param folder {@link FolderJob} + * @param oldJobName existing job name. + * @param newJobName The new job name. + * @param crumbFlag true to add crumbIssuer + * false otherwise. + * @throws IOException In case of a failure. + */ + public JenkinsServer renameJob(FolderJob folder, String oldJobName, String newJobName, Boolean crumbFlag) + throws IOException { + client.post(UrlUtils.toJobBaseUrl(folder, oldJobName) + + "/doRename?newName=" + EncodingUtils.formParameter(newJobName), + crumbFlag); + return this; + } + + /** - * Rename a job - * - * @param jobName Existing Job name - * @param newJobName New Job Name - * @throws IOException In case of a failure. + * Closes underlying resources. + * Closed instances should no longer be used + * Closing an already closed instance has no side effects + */ + @Override + public void close() { + client.close(); + } + + + /** + * Restart Jenkins without waiting for any existing build to complete + * + * @param crumbFlag + * true to add crumbIssuer false + * otherwise. + * @throws IOException + * in case of an error. + */ + public JenkinsServer restart(Boolean crumbFlag) throws IOException { + try { + client.post("/restart", crumbFlag); + } catch (org.apache.http.client.ClientProtocolException e) { + LOGGER.error("restart()", e); + } + return this; + } + + /** + * safeRestart: Puts Jenkins into the quiet mode, wait for existing builds + * to be completed, and then restart Jenkins + * + * @param crumbFlag + * true to add crumbIssuer false + * otherwise. + * @throws IOException + * in case of an error. + */ + public JenkinsServer safeRestart(Boolean crumbFlag) throws IOException { + try { + client.post("/safeRestart", crumbFlag); + } catch (org.apache.http.client.ClientProtocolException e) { + LOGGER.error("safeRestart()", e); + } + return this; + } + + /** + * Shutdown Jenkins without waiting for any existing build to complete + * + * @param crumbFlag + * true to add crumbIssuer false + * otherwise. + * @throws IOException + * in case of an error. + */ + public JenkinsServer exit(Boolean crumbFlag) throws IOException { + try { + client.post("/exit", crumbFlag); + } catch (org.apache.http.client.ClientProtocolException e) { + LOGGER.error("exit()", e); + } + return this; + } + + /** + * safeExit: Puts Jenkins into the quiet mode, wait for existing builds to + * be completed, and then shut down Jenkins + * + * @param crumbFlag + * true to add crumbIssuer false + * otherwise. + * @throws IOException + * in case of an error. */ - public void renameJob(String jobName, String newJobName) throws IOException { - client.post( - "/job/" + EncodingUtils.encode(jobName) + "/doRename?newName=" + EncodingUtils.encodeParam(newJobName)); + public JenkinsServer safeExit(Boolean crumbFlag) throws IOException { + try { + client.post("/safeExit", crumbFlag); + } catch (org.apache.http.client.ClientProtocolException e) { + LOGGER.error("safeExit()", e); + } + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsTriggerHelper.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsTriggerHelper.java new file mode 100644 index 00000000..8084eafb --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/JenkinsTriggerHelper.java @@ -0,0 +1,191 @@ +package com.offbytwo.jenkins; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +import com.offbytwo.jenkins.model.Build; +import com.offbytwo.jenkins.model.BuildResult; +import com.offbytwo.jenkins.model.BuildWithDetails; +import com.offbytwo.jenkins.model.JobWithDetails; +import com.offbytwo.jenkins.model.QueueItem; +import com.offbytwo.jenkins.model.QueueReference; + +/** + * collection of convenient methods which use methods from {@link JenkinsServer} + * etc. + * + * @author Karl Heinz Marbaise + * + */ +public class JenkinsTriggerHelper { + + private final JenkinsServer server; + private final Long retryInterval; + private static final Long DEFAULT_RETRY_INTERVAL = 200L; + + public JenkinsTriggerHelper(JenkinsServer server) { + this.server = server; + this.retryInterval = DEFAULT_RETRY_INTERVAL; + } + + public JenkinsTriggerHelper(JenkinsServer server, Long retryInterval) { + this.server = server; + this.retryInterval = retryInterval; + } + + /** + * This method will trigger a build of the given job and will wait until the + * builds is ended or if the build has been cancelled. + * + * @param jobName The name of the job which should be triggered. + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName) throws IOException, InterruptedException { + return triggerJobAndWaitUntilFinished(jobName, false); + } + + /** + * This method will trigger a build of the given job and will wait until the + * builds is ended or if the build has been cancelled. + * + * @param jobName The name of the job which should be triggered. + * @param params the job parameters + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, Map params) + throws IOException, InterruptedException { + return triggerJobAndWaitUntilFinished(jobName, params, false); + } + + /** + * This method will trigger a build of the given job and will wait until the + * builds is ended or if the build has been cancelled. + * + * @param jobName The name of the job which should be triggered. + * @param params the job parameters + * @param crumbFlag set to true or false. + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, Map params, + boolean crumbFlag) throws IOException, InterruptedException { + JobWithDetails job = this.server.getJob(jobName); + QueueReference queueRef = job.build(params, crumbFlag); + + return triggerJobAndWaitUntilFinished(jobName, queueRef); + } + + /** + * This method will trigger a build of the given job and will wait until the + * builds is ended or if the build has been cancelled. + * + * @param jobName The name of the job which should be triggered. + * @param params the job parameters + * @param fileParams the job file parameters + * @param crumbFlag set to true or false. + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, Map params, + Map fileParams, + boolean crumbFlag) throws IOException, InterruptedException { + JobWithDetails job = this.server.getJob(jobName); + QueueReference queueRef = job.build(params, fileParams, crumbFlag); + + return triggerJobAndWaitUntilFinished(jobName, queueRef); + } + + /** + * This method will trigger a build of the given job and will wait until the + * builds is ended or if the build has been cancelled. + * + * @param jobName The name of the job which should be triggered. + * @param params the job parameters + * @param fileParams the job file parameters + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, Map params, + Map fileParams) throws IOException, InterruptedException { + return triggerJobAndWaitUntilFinished(jobName, params, fileParams, false); + } + + /** + * This method will trigger a build of the given job and will wait until the + * builds is ended or if the build has been cancelled. + * + * @param jobName The name of the job which should be triggered. + * @param crumbFlag set to true or false. + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + public BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, boolean crumbFlag) + throws IOException, InterruptedException { + JobWithDetails job = this.server.getJob(jobName); + QueueReference queueRef = job.build(crumbFlag); + + return triggerJobAndWaitUntilFinished(jobName, queueRef); + } + + /** + * @param jobName The name of the job. + * @param queueRef {@link QueueReference} + * @return In case of an cancelled job you will get + * {@link BuildWithDetails#getResult()} + * {@link BuildResult#CANCELLED}. So you have to check first if the + * build result is {@code CANCELLED}. + * @throws IOException in case of errors. + * @throws InterruptedException In case of interrupts. + */ + private BuildWithDetails triggerJobAndWaitUntilFinished(String jobName, QueueReference queueRef) + throws IOException, InterruptedException { + JobWithDetails job = this.server.getJob(jobName); + QueueItem queueItem = this.server.getQueueItem(queueRef); + + while (!queueItem.isCancelled() && job.isInQueue()) { + Thread.sleep(retryInterval); + job = this.server.getJob(jobName); + queueItem = this.server.getQueueItem(queueRef); + } + + Build build = server.getBuild(queueItem); + if (queueItem.isCancelled()) { + return build.details(); + } + + boolean isBuilding = build.details().isBuilding(); + while (isBuilding) { + Thread.sleep(retryInterval); + isBuilding = build.details().isBuilding(); + } + + return build.details(); + } +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java old mode 100644 new mode 100755 index 5a3703e5..4eadc5c9 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpClient.java @@ -3,80 +3,80 @@ * * Distributed under the MIT license: http://opensource.org/licenses/MIT */ - package com.offbytwo.jenkins.client; -import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; -import static org.apache.commons.lang.StringUtils.isNotBlank; - -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.util.List; -import java.util.Map; - +import com.fasterxml.jackson.databind.ObjectMapper; +import com.offbytwo.jenkins.client.util.EncodingUtils; +import com.offbytwo.jenkins.client.util.RequestReleasingInputStream; +import com.offbytwo.jenkins.client.validator.HttpResponseValidator; +import com.offbytwo.jenkins.model.BaseModel; +import com.offbytwo.jenkins.model.Crumb; +import com.offbytwo.jenkins.model.ExtractHeader; +import net.sf.json.JSONObject; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; -import org.apache.http.Header; +import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; +import org.apache.http.entity.mime.HttpMultipartMode; +import org.apache.http.entity.mime.MultipartEntityBuilder; +import org.apache.http.entity.mime.content.FileBody; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicHeader; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpParams; import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.HttpContext; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.common.collect.Lists; -import com.google.common.io.ByteStreams; -import com.offbytwo.jenkins.client.util.EncodingUtils; -import com.offbytwo.jenkins.client.util.RequestReleasingInputStream; -//import com.offbytwo.jenkins.client.util.HttpResponseContentExtractor; -import com.offbytwo.jenkins.client.validator.HttpResponseValidator; -import com.offbytwo.jenkins.model.BaseModel; -import com.offbytwo.jenkins.model.Crumb; -import com.offbytwo.jenkins.model.ExtractHeader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; -import net.sf.json.JSONObject; +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; +import com.offbytwo.jenkins.client.util.ResponseUtils; +import com.offbytwo.jenkins.client.util.UrlUtils; +import static org.apache.commons.lang.StringUtils.isNotBlank; -public class JenkinsHttpClient { - private final Logger LOGGER = LoggerFactory.getLogger( getClass() ); - - private static final int SO_TIMEOUT_IN_MILLISECONDS = 3000; - private static final int CONNECTION_TIMEOUT_IN_MILLISECONDS = 500; +public class JenkinsHttpClient implements JenkinsHttpConnection { + + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); private URI uri; private CloseableHttpClient client; - private BasicHttpContext localContext; + private HttpContext localContext; private HttpResponseValidator httpResponseValidator; // private HttpResponseContentExtractor contentExtractor; private ObjectMapper mapper; private String context; - + private String jenkinsVersion; + public final static String EMPTY_VERSION = "UNKNOWN"; + /** * Create an unauthenticated Jenkins HTTP client * - * @param uri - * Location of the jenkins server (ex. http://localhost:8080) - * @param client - * Configured CloseableHttpClient to be used + * @param uri Location of the jenkins server (ex. http://localhost:8080) + * @param client Configured CloseableHttpClient to be used */ public JenkinsHttpClient(URI uri, CloseableHttpClient client) { this.context = uri.getPath(); @@ -88,17 +88,15 @@ public JenkinsHttpClient(URI uri, CloseableHttpClient client) { this.client = client; this.httpResponseValidator = new HttpResponseValidator(); // this.contentExtractor = new HttpResponseContentExtractor(); - this.jenkinsVersion = null; + this.jenkinsVersion = EMPTY_VERSION; LOGGER.debug("uri={}", uri.toString()); } /** * Create an unauthenticated Jenkins HTTP client * - * @param uri - * Location of the jenkins server (ex. http://localhost:8080) - * @param builder - * Configured HttpClientBuilder to be used + * @param uri Location of the jenkins server (ex. http://localhost:8080) + * @param builder Configured HttpClientBuilder to be used */ public JenkinsHttpClient(URI uri, HttpClientBuilder builder) { this(uri, builder.build()); @@ -107,39 +105,33 @@ public JenkinsHttpClient(URI uri, HttpClientBuilder builder) { /** * Create an unauthenticated Jenkins HTTP client * - * @param uri - * Location of the jenkins server (ex. http://localhost:8080) + * @param uri Location of the jenkins server (ex. http://localhost:8080) */ public JenkinsHttpClient(URI uri) { this(uri, HttpClientBuilder.create()); - this.context = uri.getPath(); - - if (!context.endsWith("/")) { - context += "/"; - } - this.uri = uri; - this.mapper = getDefaultMapper(); - - HttpParams httpParams = new BasicHttpParams(); - httpParams.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, SO_TIMEOUT_IN_MILLISECONDS); - httpParams.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, CONNECTION_TIMEOUT_IN_MILLISECONDS); - - this.httpResponseValidator = new HttpResponseValidator(); - LOGGER.debug("uri={}", uri.toString()); } /** * Create an authenticated Jenkins HTTP client * - * @param uri - * Location of the jenkins server (ex. http://localhost:8080) - * @param username - * Username to use when connecting - * @param password - * Password or auth token to use when connecting + * @param uri Location of the jenkins server (ex. http://localhost:8080) + * @param username Username to use when connecting + * @param password Password or auth token to use when connecting */ public JenkinsHttpClient(URI uri, String username, String password) { - this(uri, addAuthentication(HttpClientBuilder.create(), uri, username, password)); + this(uri, HttpClientBuilder.create(), username, password); + } + + /** + * Create an authenticated Jenkins HTTP client + * + * @param uri Location of the jenkins server (ex. http://localhost:8080) + * @param builder Configured HttpClientBuilder to be used + * @param username Username to use when connecting + * @param password Password or auth token to use when connecting + */ + public JenkinsHttpClient(URI uri, HttpClientBuilder builder, String username, String password) { + this(uri, addAuthentication(builder, uri, username, password)); if (isNotBlank(username)) { localContext = new BasicHttpContext(); localContext.setAttribute("preemptive-auth", new BasicScheme()); @@ -147,22 +139,14 @@ public JenkinsHttpClient(URI uri, String username, String password) { } /** - * Perform a GET request and parse the response to the given class - * - * @param path - * path to request, can be relative or absolute - * @param cls - * class of the response - * @param - * type of the response - * @return an instance of the supplied class - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override public T get(String path, Class cls) throws IOException { - HttpGet getMethod = new HttpGet(api(path)); + HttpGet getMethod = new HttpGet(UrlUtils.toJsonApiUri(uri, context, path)); + HttpResponse response = client.execute(getMethod, localContext); - getJenkinsVersionFromHeader(response); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); try { httpResponseValidator.validateResponse(response); return objectFromResponse(cls, response); @@ -173,20 +157,15 @@ public T get(String path, Class cls) throws IOException } /** - * Perform a GET request and parse the response and return a simple string - * of the content - * - * @param path - * path to request, can be relative or absolute - * @return the entity text - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override public String get(String path) throws IOException { - HttpGet getMethod = new HttpGet(api(path)); + HttpGet getMethod = new HttpGet(UrlUtils.toJsonApiUri(uri, context, path)); HttpResponse response = client.execute(getMethod, localContext); - getJenkinsVersionFromHeader(response); - LOGGER.debug("get({}), version={}, responseCode={}", path, this.jenkinsVersion, response.getStatusLine().getStatusCode()); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); + LOGGER.debug("get({}), version={}, responseCode={}", path, this.jenkinsVersion, + response.getStatusLine().getStatusCode()); try { httpResponseValidator.validateResponse(response); return IOUtils.toString(response.getEntity().getContent()); @@ -198,17 +177,9 @@ public String get(String path) throws IOException { } /** - * Perform a GET request and parse the response to the given class, logging - * any IOException that is thrown rather than propagating it. - * - * @param path - * path to request, can be relative or absolute - * @param cls - * class of the response - * @param - * type of the response - * @return an instance of the supplied class + * {@inheritDoc} */ + @Override public T getQuietly(String path, Class cls) { T value; try { @@ -216,65 +187,69 @@ public T getQuietly(String path, Class cls) { return value; } catch (IOException e) { LOGGER.debug("getQuietly({}, {})", path, cls.getName(), e); - //TODO: Is returing null a good idea? + // TODO: Is returing null a good idea? return null; } } /** - * Perform a GET request and return the response as InputStream - * - * @param path - * path to request, can be relative or absolute - * @return the response stream - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override public InputStream getFile(URI path) throws IOException { HttpGet getMethod = new HttpGet(path); HttpResponse response = client.execute(getMethod, localContext); - getJenkinsVersionFromHeader(response); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); httpResponseValidator.validateResponse(response); return new RequestReleasingInputStream(response.getEntity().getContent(), getMethod); } + /** + * {@inheritDoc} + */ + @Override public R post(String path, D data, Class cls) throws IOException { - return post(path, data, cls, true); + return post(path, data, cls, null, true); } /** - * Perform a POST request and parse the response to the given class - * - * @param path - * path to request, can be relative or absolute - * @param data - * data to post - * @param cls - * class of the response - * @param - * type of the response - * @param - * type of the data - * @return an instance of the supplied class - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override public R post(String path, D data, Class cls, boolean crumbFlag) throws IOException { - HttpPost request = new HttpPost(api(path)); - if (crumbFlag == true) { - Crumb crumb = getQuietly("/crumbIssuer", Crumb.class); - if (crumb != null) { - request.addHeader(new BasicHeader(crumb.getCrumbRequestField(), crumb.getCrumb())); - } - } + return post(path, data, cls, null, crumbFlag); + } + + /** + * {@inheritDoc} + */ + @Override + public R post(String path, D data, Class cls, Map fileParams, boolean crumbFlag) throws IOException { + HttpPost request = new HttpPost(UrlUtils.toJsonApiUri(uri, context, path)); + handleCrumbFlag(crumbFlag, request); if (data != null) { String value = mapper.writeValueAsString(data); StringEntity stringEntity = new StringEntity(value, ContentType.APPLICATION_JSON); request.setEntity(stringEntity); } + + // Prepare file parameters + if(fileParams != null && !(fileParams.isEmpty())) { + MultipartEntityBuilder builder = MultipartEntityBuilder.create(); + builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); + + for (Map.Entry entry : fileParams.entrySet()) { + FileBody fileBody = new FileBody(entry.getValue()); + builder.addPart(entry.getKey(), fileBody); + } + + HttpEntity entity = builder.build(); + request.setEntity(entity); + } + HttpResponse response = client.execute(request, localContext); - getJenkinsVersionFromHeader(response); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); try { httpResponseValidator.validateResponse(response); @@ -298,54 +273,42 @@ public R post(String path, D data, Class cls, boolea } } + private void handleCrumbFlag(boolean crumbFlag, HttpPost request) { + if (crumbFlag) { + Crumb crumb = getQuietly("/crumbIssuer", Crumb.class); + if (crumb != null) { + request.addHeader(new BasicHeader(crumb.getCrumbRequestField(), crumb.getCrumb())); + } + } + } + /** - * Perform a POST request using form url encoding. - * - * This method was added for the purposes of creating folders, but may be - * useful for other API calls as well. - * - * Unlike post and post_xml, the path is *not* modified by adding - * "/api/json". Additionally, the params in data are provided as both - * request parameters including a json parameter, *and* in the - * JSON-formatted StringEntity, because this is what the folder creation - * call required. It is unclear if any other jenkins APIs operate in this - * fashion. - * - * @param path - * path to request, can be relative or absolute - * @param data - * data to post - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override public void post_form(String path, Map data, boolean crumbFlag) throws IOException { HttpPost request; if (data != null) { // https://gist.github.com/stuart-warren/7786892 was slightly // helpful here - List queryParams = Lists.newArrayList(); + List queryParams = new ArrayList<>(); for (String param : data.keySet()) { - queryParams.add(param + "=" + EncodingUtils.encodeParam(data.get(param))); + queryParams.add(param + "=" + EncodingUtils.formParameter(data.get(param))); } - queryParams.add("json=" + EncodingUtils.encodeParam(JSONObject.fromObject(data).toString())); + queryParams.add("json=" + EncodingUtils.formParameter(JSONObject.fromObject(data).toString())); String value = mapper.writeValueAsString(data); StringEntity stringEntity = new StringEntity(value, ContentType.APPLICATION_FORM_URLENCODED); - request = new HttpPost(noapi(path) + StringUtils.join(queryParams, "&")); + request = new HttpPost(UrlUtils.toNoApiUri(uri, context, path) + StringUtils.join(queryParams, "&")); request.setEntity(stringEntity); } else { - request = new HttpPost(noapi(path)); + request = new HttpPost(UrlUtils.toNoApiUri(uri, context, path)); } - if (crumbFlag == true) { - Crumb crumb = get("/crumbIssuer", Crumb.class); - if (crumb != null) { - request.addHeader(new BasicHeader(crumb.getCrumbRequestField(), crumb.getCrumb())); - } - } + handleCrumbFlag(crumbFlag, request); HttpResponse response = client.execute(request, localContext); - getJenkinsVersionFromHeader(response); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); try { httpResponseValidator.validateResponse(response); @@ -356,37 +319,48 @@ public void post_form(String path, Map data, boolean crumbFlag) } /** - * Perform a POST request of XML (instead of using json mapper) and return a - * string rendering of the response entity. - * - * @param path - * path to request, can be relative or absolute - * @param xml_data - * data data to post - * @return A string containing the xml response (if present) - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override + public HttpResponse post_form_with_result(String path, List data, boolean crumbFlag) throws IOException { + HttpPost request; + if (data != null) { + UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(data); + request = new HttpPost(UrlUtils.toNoApiUri(uri, context, path)); + request.setEntity(urlEncodedFormEntity); + } else { + request = new HttpPost(UrlUtils.toNoApiUri(uri, context, path)); + } + + handleCrumbFlag(crumbFlag, request); + HttpResponse response = client.execute(request, localContext); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); + return response; + } + + /** + * {@inheritDoc} + */ + @Override public String post_xml(String path, String xml_data) throws IOException { return post_xml(path, xml_data, true); } + /** + * {@inheritDoc} + */ + @Override public String post_xml(String path, String xml_data, boolean crumbFlag) throws IOException { - HttpPost request = new HttpPost(api(path)); - if (crumbFlag == true) { - Crumb crumb = getQuietly("/crumbIssuer", Crumb.class); - if (crumb != null) { - request.addHeader(new BasicHeader(crumb.getCrumbRequestField(), crumb.getCrumb())); - } - } + HttpPost request = new HttpPost(UrlUtils.toJsonApiUri(uri, context, path)); + handleCrumbFlag(crumbFlag, request); if (xml_data != null) { request.setEntity(new StringEntity(xml_data, ContentType.create("text/xml", "utf-8"))); } HttpResponse response = client.execute(request, localContext); - getJenkinsVersionFromHeader(response); - httpResponseValidator.validateResponse(response); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); try { + httpResponseValidator.validateResponse(response); return IOUtils.toString(response.getEntity().getContent()); } finally { EntityUtils.consume(response.getEntity()); @@ -395,44 +369,29 @@ public String post_xml(String path, String xml_data, boolean crumbFlag) throws I } /** - * Post a text entity to the given URL using the default content type - * - * @param path - * @param textData - * @param crumbFlag - * @return resulting response - * @throws IOException + * {@inheritDoc} */ + @Override public String post_text(String path, String textData, boolean crumbFlag) throws IOException { return post_text(path, textData, ContentType.DEFAULT_TEXT, crumbFlag); } /** - * Post a text entity to the given URL with the given content type - * - * @param path - * @param textData - * @param crumbFlag - * @return resulting response - * @throws IOException + * {@inheritDoc} */ + @Override public String post_text(String path, String textData, ContentType contentType, boolean crumbFlag) throws IOException { - HttpPost request = new HttpPost(api(path)); - if (crumbFlag == true) { - Crumb crumb = get("/crumbIssuer", Crumb.class); - if (crumb != null) { - request.addHeader(new BasicHeader(crumb.getCrumbRequestField(), crumb.getCrumb())); - } - } + HttpPost request = new HttpPost(UrlUtils.toJsonApiUri(uri, context, path)); + handleCrumbFlag(crumbFlag, request); if (textData != null) { request.setEntity(new StringEntity(textData, contentType)); } HttpResponse response = client.execute(request, localContext); - getJenkinsVersionFromHeader(response); - httpResponseValidator.validateResponse(response); + jenkinsVersion = ResponseUtils.getJenkinsVersion(response); try { + httpResponseValidator.validateResponse(response); return IOUtils.toString(response.getEntity().getContent()); } finally { EntityUtils.consume(response.getEntity()); @@ -441,54 +400,100 @@ public String post_text(String path, String textData, ContentType contentType, b } /** - * Perform POST request that takes no parameters and returns no response - * - * @param path - * path to request - * @throws IOException, - * HttpResponseException + * {@inheritDoc} */ + @Override public void post(String path) throws IOException { - post(path, null, null, false); + post(path, null, null, null, false); } + /** + * {@inheritDoc} + */ + @Override public void post(String path, boolean crumbFlag) throws IOException { - post(path, null, null, crumbFlag); + post(path, null, null,null, crumbFlag); } - private String urlJoin(String path1, String path2) { - if (!path1.endsWith("/")) { - path1 += "/"; - } - if (path2.startsWith("/")) { - path2 = path2.substring(1); - } - return path1 + path2; + /** + * {@inheritDoc} + */ + @Override + public String getJenkinsVersion() { + return this.jenkinsVersion; } - private URI api(String path) { - if (!path.toLowerCase().matches("https?://.*")) { - path = urlJoin(this.context, path); - } - if (!path.contains("?")) { - path = urlJoin(path, "api/json"); - } else { - String[] components = path.split("\\?", 2); - path = urlJoin(components[0], "api/json") + "?" + components[1]; + /** + * {@inheritDoc} + */ + @Override + public boolean isJenkinsVersionSet() { + return !EMPTY_VERSION.equals(this.jenkinsVersion); + } + + /** + * Closes underlying resources. Any I/O errors whilst closing are logged + * with debug log level Closed instances should no longer be used Closing an + * already closed instance has no side effects + */ + @Override + public void close() { + try { + client.close(); + } catch (final IOException ex) { + LOGGER.debug("I/O exception closing client", ex); } - return uri.resolve("/").resolve(path.replace(" ","%20")); } - private URI noapi(String path) { - if (!path.toLowerCase().matches("https?://.*")) { - path = urlJoin(this.context, path); + + /** + * Add authentication to supplied builder. + * @param builder the builder to configure + * @param uri the server URI + * @param username the username + * @param password the password + * @return the passed in builder + */ + protected static HttpClientBuilder addAuthentication(final HttpClientBuilder builder, + final URI uri, final String username, + String password) { + if (isNotBlank(username)) { + CredentialsProvider provider = new BasicCredentialsProvider(); + AuthScope scope = new AuthScope(uri.getHost(), uri.getPort(), "realm"); + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); + provider.setCredentials(scope, credentials); + builder.setDefaultCredentialsProvider(provider); + + builder.addInterceptorFirst(new PreemptiveAuth()); } - return uri.resolve("/").resolve(path); + return builder; } + + /** + * Get the local context. + * @return context + */ + protected HttpContext getLocalContext() { + return localContext; + } + + + /** + * Set the local context. + * @param localContext the context + */ + protected void setLocalContext(final HttpContext localContext) { + this.localContext = localContext; + } + + + + + private T objectFromResponse(Class cls, HttpResponse response) throws IOException { InputStream content = response.getEntity().getContent(); - byte[] bytes = ByteStreams.toByteArray(content); + byte[] bytes = IOUtils.toByteArray(content); T result = mapper.readValue(bytes, cls); // TODO: original: // T result = mapper.readValue(content, cls); @@ -502,35 +507,8 @@ private ObjectMapper getDefaultMapper() { return mapper; } - /** - * @return the version string. - */ - public String getJenkinsVersion() { - return this.jenkinsVersion; - } - - private void getJenkinsVersionFromHeader(HttpResponse response) { - Header[] headers = response.getHeaders("X-Jenkins"); - if (headers.length == 1) { - this.jenkinsVersion = headers[0].getValue(); - } - } - private void releaseConnection(HttpRequestBase httpRequestBase) { httpRequestBase.releaseConnection(); } - private static HttpClientBuilder addAuthentication(HttpClientBuilder builder, URI uri, String username, - String password) { - if (isNotBlank(username)) { - CredentialsProvider provider = new BasicCredentialsProvider(); - AuthScope scope = new AuthScope(uri.getHost(), uri.getPort(), "realm"); - UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(username, password); - provider.setCredentials(scope, credentials); - builder.setDefaultCredentialsProvider(provider); - - builder.addInterceptorFirst(new PreemptiveAuth()); - } - return builder; - } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpConnection.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpConnection.java new file mode 100644 index 00000000..b90bfcaf --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/JenkinsHttpConnection.java @@ -0,0 +1,230 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.offbytwo.jenkins.client; + +import com.offbytwo.jenkins.model.BaseModel; +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.List; +import java.util.Map; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.entity.ContentType; + +/** + * + * @author Dell Green + */ +public interface JenkinsHttpConnection extends Closeable { + + + + /** + * {@inheritDoc} + */ + @Override + public void close(); + + + /** + * Perform a GET request and parse the response to the given class + * + * @param path path to request, can be relative or absolute + * @param cls class of the response + * @param type of the response + * @return an instance of the supplied class + * @throws IOException in case of an error. + */ + T get(String path, Class cls) throws IOException; + + /** + * Perform a GET request and parse the response and return a simple string + * of the content + * + * @param path path to request, can be relative or absolute + * @return the entity text + * @throws IOException in case of an error. + */ + String get(String path) throws IOException; + + /** + * Perform a GET request and return the response as InputStream + * + * @param path path to request, can be relative or absolute + * @return the response stream + * @throws IOException in case of an error. + */ + InputStream getFile(URI path) throws IOException; + + /** + * Perform a GET request and parse the response to the given class, logging + * any IOException that is thrown rather than propagating it. + * + * @param path path to request, can be relative or absolute + * @param cls class of the response + * @param type of the response + * @return an instance of the supplied class + */ + T getQuietly(String path, Class cls); + + /** + * Check to see if the Jenkins version has been set to something different + * than the initialisation value from the constructor. This means there has + * never been made a communication with the Jenkins server. + * @return true if jenkinsVersion has been set by communication, false + * otherwise. + */ + boolean isJenkinsVersionSet(); + + + /** + * Perform a POST request and parse the response to the given class + * + * @param path path to request, can be relative or absolute + * @param data data to post + * @param cls class of the response + * @param type of the response + * @param type of the data + * @return an instance of the supplied class + * @throws IOException in case of an error. + */ + R post(String path, D data, Class cls) throws IOException; + + /** + * Perform a POST request and parse the response to the given class + * + * @param path path to request, can be relative or absolute + * @param data data to post + * @param cls class of the response + * @param type of the response + * @param type of the data + * @param crumbFlag true / false. + * @return an instance of the supplied class + * @throws IOException in case of an error. + */ + R post(String path, D data, Class cls, boolean crumbFlag) throws IOException; + + /** + * Perform a POST request and parse the response to the given class + * + * @param path path to request, can be relative or absolute + * @param data data to post + * @param cls class of the response + * @param fileParams file parameters + * @param type of the response + * @param type of the data + * @param crumbFlag true / false. + * @return an instance of the supplied class + * @throws IOException in case of an error. + */ + R post(String path, D data, Class cls, Map fileParams, boolean crumbFlag) throws IOException; + + /** + * Perform POST request that takes no parameters and returns no response + * + * @param path path to request + * @throws IOException in case of an error. + */ + void post(String path) throws IOException; + + + /** + * Perform POST request that takes no parameters and returns no response + * + * @param path path to request + * @param crumbFlag true / false. + * @throws IOException in case of an error. + */ + void post(String path, boolean crumbFlag) throws IOException; + + /** + * Perform a POST request using form url encoding. + * + * This method was added for the purposes of creating folders, but may be + * useful for other API calls as well. Unlike post and post_xml, the path is + * *not* modified by adding "/toJsonApiUri/json". Additionally, the params + * in data are provided as both request parameters including a json + * formParameter, *and* in the JSON-formatted StringEntity, because this is what + * the folder creation call required. It is unclear if any other jenkins + * APIs operate in this fashion. + * + * @param path path to request, can be relative or absolute + * @param data data to post + * @param crumbFlag true / false. + * @throws IOException in case of an error. + */ + void post_form(String path, Map data, boolean crumbFlag) throws IOException; + + /** + * Perform a POST request using form url encoding and return HttpResponse + * object This method is not performing validation and can be used for more + * generic queries to jenkins. + * + * @param path path to request, can be relative or absolute + * @param data data to post + * @param crumbFlag true / false. + * @return resulting response + * @throws IOException in case of an error. + */ + HttpResponse post_form_with_result(String path, List data, boolean crumbFlag) throws IOException; + + /** + * Post a text entity to the given URL using the default content type + * + * @param path The path. + * @param textData data. + * @param crumbFlag true/false. + * @return resulting response + * @throws IOException in case of an error. + */ + String post_text(String path, String textData, boolean crumbFlag) throws IOException; + + /** + * Post a text entity to the given URL with the given content type + * + * @param path The path. + * @param textData The data. + * @param contentType {@link ContentType} + * @param crumbFlag true or false. + * @return resulting response + * @throws IOException in case of an error. + */ + String post_text(String path, String textData, ContentType contentType, boolean crumbFlag) throws IOException; + + /** + * Perform a POST request of XML (instead of using json mapper) and return a + * string rendering of the response entity. + * + * @param path path to request, can be relative or absolute + * @param xml_data data data to post + * @return A string containing the xml response (if present) + * @throws IOException in case of an error. + */ + String post_xml(String path, String xml_data) throws IOException; + + /** + * Perform a POST request of XML (instead of using json mapper) and return a + * string rendering of the response entity. + * + * @param path path to request, can be relative or absolute + * @param xml_data data data to post + * @param crumbFlag true or false. + * @return A string containing the xml response (if present) + * @throws IOException in case of an error. + */ + String post_xml(String path, String xml_data, boolean crumbFlag) throws IOException; + + /** + * Get the Jenkins server version. + * + * @return the version string + */ + String getJenkinsVersion(); + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/PreemptiveAuth.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/PreemptiveAuth.java index d14b0dcd..ce772ca9 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/PreemptiveAuth.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/PreemptiveAuth.java @@ -21,7 +21,7 @@ import java.io.IOException; -class PreemptiveAuth implements HttpRequestInterceptor { +public class PreemptiveAuth implements HttpRequestInterceptor { @Override public void process(HttpRequest request, HttpContext context) throws HttpException, IOException { diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/EncodingUtils.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/EncodingUtils.java index 25661294..4638634b 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/EncodingUtils.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/EncodingUtils.java @@ -1,7 +1,21 @@ +/* + * Copyright (c) 2019 Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ package com.offbytwo.jenkins.client.util; -import com.google.common.net.UrlEscapers; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +/** + * This class is a help class to centralize the + * encoding parts which will call an appropriate library function. + * + * @author Karl Heinz Marbaise + */ public final class EncodingUtils { private EncodingUtils() { @@ -9,13 +23,24 @@ private EncodingUtils() { public static String encode(String pathPart) { // jenkins doesn't like the + for space, use %20 instead - String escape = UrlEscapers.urlPathSegmentEscaper().escape(pathPart); - return escape; + try { + return URLEncoder.encode(pathPart, StandardCharsets.UTF_8.displayName()); + } catch (UnsupportedEncodingException e) { + // Should never happen, because that would imply that + // the parameter StandardCharsets.UTF_8 is wrong. + throw new IllegalArgumentException(e); + } } - public static String encodeParam(String pathPart) { + public static String formParameter(String pathPart) { // jenkins doesn't like the + for space, use %20 instead - return UrlEscapers.urlFormParameterEscaper().escape(pathPart); + try { + return URLEncoder.encode(pathPart, StandardCharsets.UTF_8.displayName()); + } catch (UnsupportedEncodingException e) { + // Should never happen, because that would imply that + // the parameter StandardCharsets.UTF_8 is wrong. + throw new IllegalArgumentException(e); + } } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/ResponseUtils.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/ResponseUtils.java new file mode 100644 index 00000000..572ff4b6 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/ResponseUtils.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.client.util; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; + +/** + * + * @author Dell Green + */ +public final class ResponseUtils { + + /** + * Utility Class. + */ + private ResponseUtils() { + //do nothing + } + + + + + /** + * Get Jenkins version from supplied response if any. + * @param response the response + * @return the version or empty string + */ + public static String getJenkinsVersion(final HttpResponse response) { + final Header[] hdrs = response.getHeaders("X-Jenkins"); + return hdrs.length == 0 ? "" : hdrs[0].getValue(); + } + + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/UrlUtils.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/UrlUtils.java new file mode 100644 index 00000000..6f8b17d9 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/client/util/UrlUtils.java @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + +package com.offbytwo.jenkins.client.util; + +import com.offbytwo.jenkins.model.FolderJob; +import java.net.URI; + +/** + * Utility class for manipulating API paths. + * @author Dell Green + */ +public final class UrlUtils { + + /** + * The default size to use for string buffers. + */ + private static final int DEFAULT_BUFFER_SIZE = 64; + + /** + * Utility Class. + */ + private UrlUtils() { + //do nothing + } + + + + /** + * Helper to create a base url in case a folder is given + * @param folder the folder or {@code null} + * @return The created base url. + */ + public static String toBaseUrl(final FolderJob folder) { + return folder == null ? "/" : folder.getUrl(); + } + + + + /** + * Helper to create the base url for a job, with or without a given folder + * @param folder the folder or {@code null} + * @param jobName the name of the job. + * @return converted base url. + */ + public static String toJobBaseUrl(final FolderJob folder, final String jobName) { + final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE); + sb.append(UrlUtils.toBaseUrl(folder)); + if (sb.charAt(sb.length() - 1) != '/') sb.append('/'); + sb.append("job/"); + final String[] jobNameParts = jobName.split("/"); + + for (int i = 0; i < jobNameParts.length; i++) { + sb.append(EncodingUtils.encode(jobNameParts[i])); + if (i != jobNameParts.length - 1) sb.append('/'); + } + return sb.toString(); + } + + + /** + * Helper to create the base url for a view, with or without a given folder + * @param folder the folder or {@code null} + * @param name the of the view. + * @return converted view url. + */ + public static String toViewBaseUrl(final FolderJob folder, final String name) { + final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE); + final String base = UrlUtils.toBaseUrl(folder); + sb.append(base); + if (!base.endsWith("/")) sb.append('/'); + sb.append("view/") + .append(EncodingUtils.encode(name)); + return sb.toString(); + } + + + /** + * Parses the provided job name for folders to get the full path for the job. + * @param jobName the fullName of the job. + * @return the path of the job including folders if present. + */ + public static String toFullJobPath(final String jobName) { + final String[] parts = jobName.split("/"); + if (parts.length == 1) return parts[0]; + final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE); + + for (int i = 0; i < parts.length; i++) { + sb.append(parts[i]); + if (i != parts.length -1) sb.append("/job/"); + } + return sb.toString(); + } + + + + /** + * Join two paths together taking into account leading/trailing slashes. + * @param path1 the first path + * @param path2 the second path + * @return the joins path + */ + public static String join(final String path1, final String path2) { + if (path1.isEmpty() && path2.isEmpty()) return ""; + if (path1.isEmpty() && !path2.isEmpty()) return path2; + if (path2.isEmpty() && !path1.isEmpty()) return path1; + final StringBuilder sb = new StringBuilder(DEFAULT_BUFFER_SIZE); + sb.append(path1); + if (sb.charAt(sb.length() - 1) == '/') sb.setLength(sb.length() - 1); + if (path2.charAt(0) != '/') sb.append('/'); + sb.append(path2); + return sb.toString(); + } + + + + /** + * Create a JSON URI from the supplied parameters. + * @param uri the server URI + * @param context the server context if any + * @param path the specific API path + * @return new full URI instance + */ + public static URI toJsonApiUri(final URI uri, final String context, final String path) { + String p = path; + if (!p.matches("(?i)https?://.*")) p = join(context, p); + + if (!p.contains("?")) { + p = join(p, "api/json"); + } else { + final String[] components = p.split("\\?", 2); + p = join(components[0], "api/json") + "?" + components[1]; + } + return uri.resolve("/").resolve(p.replace(" ", "%20")); + } + + + + /** + * Create a URI from the supplied parameters. + * @param uri the server URI + * @param context the server context if any + * @param path the specific API path + * @return new full URI instance + */ + public static URI toNoApiUri(final URI uri, final String context, final String path) { + final String p = path.matches("(?i)https?://.*") ? path : join(context, path); + return uri.resolve("/").resolve(p); + } + + + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/BuildConsoleStreamListener.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/BuildConsoleStreamListener.java new file mode 100644 index 00000000..41671fbb --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/BuildConsoleStreamListener.java @@ -0,0 +1,19 @@ +package com.offbytwo.jenkins.helper; + +/** + * Listener interface used to obtain build console logs + */ +public interface BuildConsoleStreamListener { + + /** + * Called by api when new log data available. + * + * @param newLogChunk - string containing latest chunk of logs + */ + void onData(String newLogChunk); + + /** + * Called when streaming console output is finished + */ + void finished(); +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/ComparableVersion.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/ComparableVersion.java new file mode 100644 index 00000000..8a364777 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/ComparableVersion.java @@ -0,0 +1,503 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.offbytwo.jenkins.helper; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Properties; +import java.util.Stack; + +/** + * This code is copied from Maven Core with slight modifications. Generic implementation of version comparison. + * Features: + *
    + *
  • mixing of '-' (hyphen) and '.' (dot) separators,
  • + *
  • transition between characters and digits also constitutes a separator: + * 1.0alpha1 => [1, 0, alpha, 1]
  • + *
  • unlimited number of version components,
  • + *
  • version components in the text can be digits or strings,
  • + *
  • strings are checked for well-known qualifiers and the qualifier ordering is used for version ordering. Well-known + * qualifiers (case insensitive) are: + *
      + *
    • alpha or a
    • + *
    • beta or b
    • + *
    • milestone or m
    • + *
    • rc or cr
    • + *
    • snapshot
    • + *
    • (the empty string) or ga or final
    • + *
    • sp
    • + *
    + * Unknown qualifiers are considered after known qualifiers, with lexical order (always case insensitive),
  • + *
  • a hyphen usually precedes a qualifier, and is always less important than something preceded with a dot.
  • + *
+ * + * @see "Versioning" on Maven Wiki + * @author Kenney Westerhof + * @author Hervé Boutemy + */ +public class ComparableVersion + implements Comparable +{ + private String value; + + private String canonical; + + private ListItem items; + + interface Item + { + int INTEGER_ITEM = 0; + + int STRING_ITEM = 1; + + int LIST_ITEM = 2; + + int compareTo( Item item ); + + int getType(); + + boolean isNull(); + } + + /** + * Represents a numeric item in the version item list. + */ + static class IntegerItem + implements Item + { + private static final BigInteger BIG_INTEGER_ZERO = new BigInteger( "0" ); + + private final BigInteger value; + + public static final IntegerItem ZERO = new IntegerItem(); + + private IntegerItem() + { + this.value = BIG_INTEGER_ZERO; + } + + public IntegerItem( String str ) + { + this.value = new BigInteger( str ); + } + + public int getType() + { + return INTEGER_ITEM; + } + + public boolean isNull() + { + return BIG_INTEGER_ZERO.equals( value ); + } + + public int compareTo( Item item ) + { + if ( item == null ) + { + return BIG_INTEGER_ZERO.equals( value ) ? 0 : 1; // 1.0 == 1, 1.1 > 1 + } + + switch ( item.getType() ) + { + case INTEGER_ITEM: + return value.compareTo( ( (IntegerItem) item ).value ); + + case STRING_ITEM: + return 1; // 1.1 > 1-sp + + case LIST_ITEM: + return 1; // 1.1 > 1-1 + + default: + throw new RuntimeException( "invalid item: " + item.getClass() ); + } + } + + public String toString() + { + return value.toString(); + } + } + + /** + * Represents a string in the version item list, usually a qualifier. + */ + static class StringItem + implements Item + { + private static final List QUALIFIERS = + Arrays.asList( "alpha", "beta", "milestone", "rc", "snapshot", "", "sp" ); + + private static final Properties ALIASES = new Properties(); + static + { + ALIASES.put( "ga", "" ); + ALIASES.put( "final", "" ); + ALIASES.put( "cr", "rc" ); + } + + /** + * A comparable value for the empty-string qualifier. This one is used to determine if a given qualifier makes + * the version older than one without a qualifier, or more recent. + */ + private static final String RELEASE_VERSION_INDEX = String.valueOf( QUALIFIERS.indexOf( "" ) ); + + private String value; + + public StringItem( String value, boolean followedByDigit ) + { + if ( followedByDigit && value.length() == 1 ) + { + // a1 = alpha-1, b1 = beta-1, m1 = milestone-1 + switch ( value.charAt( 0 ) ) + { + case 'a': + value = "alpha"; + break; + case 'b': + value = "beta"; + break; + case 'm': + value = "milestone"; + break; + default: + } + } + this.value = ALIASES.getProperty( value, value ); + } + + public int getType() + { + return STRING_ITEM; + } + + public boolean isNull() + { + return ( comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ) == 0 ); + } + + /** + * Returns a comparable value for a qualifier. This method takes into account the ordering of known qualifiers + * then unknown qualifiers with lexical ordering. just returning an Integer with the index here is faster, but + * requires a lot of if/then/else to check for -1 or QUALIFIERS.size and then resort to lexical ordering. Most + * comparisons are decided by the first character, so this is still fast. If more characters are needed then it + * requires a lexical sort anyway. + * + * @param qualifier + * @return an equivalent value that can be used with lexical comparison + */ + public static String comparableQualifier( String qualifier ) + { + int i = QUALIFIERS.indexOf( qualifier ); + + return i == -1 ? ( QUALIFIERS.size() + "-" + qualifier ) : String.valueOf( i ); + } + + public int compareTo( Item item ) + { + if ( item == null ) + { + // 1-rc < 1, 1-ga > 1 + return comparableQualifier( value ).compareTo( RELEASE_VERSION_INDEX ); + } + switch ( item.getType() ) + { + case INTEGER_ITEM: + return -1; // 1.any < 1.1 ? + + case STRING_ITEM: + return comparableQualifier( value ).compareTo( comparableQualifier( ( (StringItem) item ).value ) ); + + case LIST_ITEM: + return -1; // 1.any < 1-1 + + default: + throw new RuntimeException( "invalid item: " + item.getClass() ); + } + } + + public String toString() + { + return value; + } + } + + /** + * Represents a version list item. This class is used both for the global item list and for sub-lists (which start + * with '-(number)' in the version specification). + */ + public static class ListItem + extends ArrayList + implements Item + { + public int getType() + { + return LIST_ITEM; + } + + public boolean isNull() + { + return ( size() == 0 ); + } + + void normalize() + { + for ( int i = size() - 1; i >= 0; i-- ) + { + Item lastItem = get( i ); + + if ( lastItem.isNull() ) + { + // remove null trailing items: 0, "", empty list + remove( i ); + } + else if ( !( lastItem instanceof ListItem ) ) + { + break; + } + } + } + + public int compareTo( Item item ) + { + if ( item == null ) + { + if ( size() == 0 ) + { + return 0; // 1-0 = 1- (normalize) = 1 + } + Item first = get( 0 ); + return first.compareTo( null ); + } + switch ( item.getType() ) + { + case INTEGER_ITEM: + return -1; // 1-1 < 1.0.x + + case STRING_ITEM: + return 1; // 1-1 > 1-sp + + case LIST_ITEM: + Iterator left = iterator(); + Iterator right = ( (ListItem) item ).iterator(); + + while ( left.hasNext() || right.hasNext() ) + { + Item l = left.hasNext() ? left.next() : null; + Item r = right.hasNext() ? right.next() : null; + + // if this is shorter, then invert the compare and mul with -1 + int result = l == null ? ( r == null ? 0 : -1 * r.compareTo( l ) ) : l.compareTo( r ); + + if ( result != 0 ) + { + return result; + } + } + + return 0; + + default: + throw new RuntimeException( "invalid item: " + item.getClass() ); + } + } + + public String toString() + { + StringBuilder buffer = new StringBuilder(); + for ( Item item : this ) + { + if ( buffer.length() > 0 ) + { + buffer.append( ( item instanceof ListItem ) ? '-' : '.' ); + } + buffer.append( item ); + } + return buffer.toString(); + } + } + + public ComparableVersion( String version ) + { + parseVersion( version ); + } + + public final void parseVersion( String version ) + { + this.value = version; + + items = new ListItem(); + + version = version.toLowerCase( Locale.ENGLISH ); + + ListItem list = items; + + Stack stack = new Stack<>(); + stack.push( list ); + + boolean isDigit = false; + + int startIndex = 0; + + for ( int i = 0; i < version.length(); i++ ) + { + char c = version.charAt( i ); + + if ( c == '.' ) + { + if ( i == startIndex ) + { + list.add( IntegerItem.ZERO ); + } + else + { + list.add( parseItem( isDigit, version.substring( startIndex, i ) ) ); + } + startIndex = i + 1; + } + else if ( c == '-' ) + { + if ( i == startIndex ) + { + list.add( IntegerItem.ZERO ); + } + else + { + list.add( parseItem( isDigit, version.substring( startIndex, i ) ) ); + } + startIndex = i + 1; + + list.add( list = new ListItem() ); + stack.push( list ); + } + else if ( Character.isDigit( c ) ) + { + if ( !isDigit && i > startIndex ) + { + list.add( new StringItem( version.substring( startIndex, i ), true ) ); + startIndex = i; + + list.add( list = new ListItem() ); + stack.push( list ); + } + + isDigit = true; + } + else + { + if ( isDigit && i > startIndex ) + { + list.add( parseItem( true, version.substring( startIndex, i ) ) ); + startIndex = i; + + list.add( list = new ListItem() ); + stack.push( list ); + } + + isDigit = false; + } + } + + if ( version.length() > startIndex ) + { + list.add( parseItem( isDigit, version.substring( startIndex ) ) ); + } + + while ( !stack.isEmpty() ) + { + list = (ListItem) stack.pop(); + list.normalize(); + } + + canonical = items.toString(); + } + + private static Item parseItem( boolean isDigit, String buf ) + { + return isDigit ? new IntegerItem( buf ) : new StringItem( buf, false ); + } + + public int compareTo( ComparableVersion o ) + { + return items.compareTo( o.items ); + } + + public String toString() + { + return value; + } + + public String getCanonical() + { + return canonical; + } + + public boolean equals( Object o ) + { + return ( o instanceof ComparableVersion ) && canonical.equals( ( (ComparableVersion) o ).canonical ); + } + + public int hashCode() + { + return canonical.hashCode(); + } + + public ListItem getItems() + { + return this.items; + } + + /** + * Main to test version parsing and comparison. + * + * @param args the version strings to parse and compare + */ + public static void main( String... args ) + { + System.out.println( "Display parameters as parsed by Maven (in canonical form) and comparison result:" ); + if ( args.length == 0 ) + { + return; + } + + ComparableVersion prev = null; + int i = 1; + for ( String version : args ) + { + ComparableVersion c = new ComparableVersion( version ); + + if ( prev != null ) + { + int compare = prev.compareTo( c ); + System.out.println( " " + prev.toString() + ' ' + + ( ( compare == 0 ) ? "==" : ( ( compare < 0 ) ? "<" : ">" ) ) + ' ' + version ); + } + + System.out.println( String.valueOf( i++ ) + ". " + version + " == " + c.getCanonical() ); + + prev = c; + } + } +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/FunctionalHelper.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/FunctionalHelper.java new file mode 100644 index 00000000..d7f507b7 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/FunctionalHelper.java @@ -0,0 +1,22 @@ +package com.offbytwo.jenkins.helper; + +import com.offbytwo.jenkins.client.JenkinsHttpConnection; +import com.offbytwo.jenkins.model.BaseModel; + +import java.util.function.Function; + +public final class FunctionalHelper { + + private FunctionalHelper() { + // intentionally empty. + } + + public static final Function SET_CLIENT(JenkinsHttpConnection client) { + return s -> { + s.setClient(client); + return s; + }; + } + + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/JenkinsVersion.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/JenkinsVersion.java new file mode 100644 index 00000000..d83ce087 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/JenkinsVersion.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Karl Heinz Marbaise + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.helper; + +/** + * @author Karl Heinz Marbaise + */ +public class JenkinsVersion implements Comparable { + private ComparableVersion cv; + private String literalVersion; + + public final static JenkinsVersion create(String version) { + JenkinsVersion jv = new JenkinsVersion(version); + return jv; + } + + public JenkinsVersion() { + this.cv = new ComparableVersion("0"); + } + + public JenkinsVersion(String version) { + this.literalVersion = version; + this.cv = new ComparableVersion(version); + } + + /** + * This will check if the current instance version is > the + * given version. + * + * @param version The version to compare with. + * @return true or false. + */ + public boolean isGreaterThan(String version) { + JenkinsVersion create = create(version); + return this.cv.compareTo(create.cv) > 0; + } + + public boolean isGreaterThan(JenkinsVersion jv) { + return this.cv.compareTo(jv.cv) > 0; + } + + /** + * This will check if the current instance version is >= the + * given version. + * + * @param version The version to compare with. + * @return true or false. + */ + public boolean isGreaterOrEqual(String version) { + JenkinsVersion create = create(version); + return this.cv.compareTo(create.cv) >= 0; + } + + public boolean isGreaterOrEqual(JenkinsVersion jv) { + return this.cv.compareTo(jv.cv) >= 0; + } + + /** + * This will check if the current instance version is < the + * given version. + * + * @param version The version to compare with. + * @return true or false. + */ + public boolean isLessThan(String version) { + JenkinsVersion create = create(version); + return this.cv.compareTo(create.cv) < 0; + } + + public boolean isLessThan(JenkinsVersion jv) { + return this.cv.compareTo(jv.cv) < 0; + } + + /** + * This will check if the current instance version is <= the + * given version. + * + * @param version The version to compare with. + * @return true or false. + */ + public boolean isLessOrEqual(String version) { + JenkinsVersion create = create(version); + return this.cv.compareTo(create.cv) <= 0; + } + + public boolean isLessOrEqual(JenkinsVersion jv) { + return this.cv.compareTo(jv.cv) <= 0; + } + + /** + * This will check if the current instance version is = the + * given version. + * + * @param version The version to compare with. + * @return true or false. + */ + public boolean isEqualTo(String version) { + JenkinsVersion create = create(version); + return this.cv.compareTo(create.cv) == 0; + } + + public boolean isEqualTo(JenkinsVersion jv) { + return this.cv.compareTo(jv.cv) == 0; + } + + @Override + public int compareTo(JenkinsVersion o) { + return this.compareTo(o); + } + + public String getLiteralVersion() { + return literalVersion; + } + + @Override + public String toString() { + return literalVersion; + } +} \ No newline at end of file diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/Range.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/Range.java index 642fd8d5..cdbdc60a 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/Range.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/helper/Range.java @@ -16,21 +16,29 @@ * The same as {0,N}. *
  • {N}: Just retrieve the N-th element. The same as {N,N+1}.
  • * - * + *

    * You can use the {@link Range} class like this: - * + * *

      * Range fromAndTo = Range.build().from(1).to(5);
      * Range fromOnly = Range.build().from(3).build();
      * Range toOnly = Range.build().to(5).build();
      * Range only = Range.build().only(3);
      * 
    - * - * @author Karl Heinz Marbaise * + * @author Karl Heinz Marbaise */ public final class Range { + /** + * This represents {@code {} (left curly bracket). + */ + public static final String CURLY_BRACKET_OPEN = "%7B"; + /** + * This represents {@code }} (right curly bracket). + */ + public static final String CURLY_BRACKET_CLOSE = "%7D"; + private Integer from; private Integer to; @@ -57,7 +65,7 @@ private Range setTo(int to) { public String getRangeString() { StringBuilder sb = new StringBuilder(); - sb.append("{"); + sb.append(CURLY_BRACKET_OPEN); if (this.from != null) { sb.append(String.format("%d", this.from)); } @@ -68,7 +76,7 @@ public String getRangeString() { sb.append(String.format("%d", this.to)); } - sb.append('}'); + sb.append(CURLY_BRACKET_CLOSE); return sb.toString(); } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/AllMavenBuilds.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/AllMavenBuilds.java new file mode 100644 index 00000000..3503b0f2 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/AllMavenBuilds.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + +package com.offbytwo.jenkins.model; + +import java.util.List; + +/** + * This class is only needed to get all builds in + * {@link MavenJobWithDetails#getAllBuilds()}. + * + * @author Karl Heinz Marbaise + * + * NOTE: This class is not part of any public API + */ +class AllMavenBuilds extends BaseModel { + private List allBuilds; + + public AllMavenBuilds() { + } + + public List getAllBuilds() { + return this.allBuilds; + } +} \ No newline at end of file diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java index 89b5683d..a6c7a2f0 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Artifact.java @@ -8,32 +8,35 @@ public class Artifact extends BaseModel { - String displayPath; - String fileName; - String relativePath; + private String displayPath; + private String fileName; + private String relativePath; public String getDisplayPath() { return displayPath; } - public void setDisplayPath(String displayPath) { + public Artifact setDisplayPath(String displayPath) { this.displayPath = displayPath; + return this; } public String getFileName() { return fileName; } - public void setFileName(String fileName) { + public Artifact setFileName(String fileName) { this.fileName = fileName; + return this; } public String getRelativePath() { return relativePath; } - public void setRelativePath(String relativePath) { + public Artifact setRelativePath(String relativePath) { this.relativePath = relativePath; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BaseModel.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BaseModel.java index 14f788b5..5275d36b 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BaseModel.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BaseModel.java @@ -6,17 +6,50 @@ package com.offbytwo.jenkins.model; -import com.offbytwo.jenkins.client.JenkinsHttpClient; +import com.offbytwo.jenkins.client.JenkinsHttpConnection; +import java.util.function.Predicate; +/** + * The base model. + */ public class BaseModel { - JenkinsHttpClient client; + /** + * The class. + */ + private String _class; + + /** + * Get the class. + * @return class + */ + public String get_class() { + return _class; + } + + //TODO: We should make this private + protected JenkinsHttpConnection client; - public JenkinsHttpClient getClient() { + + /** + * Get the HTTP client. + * @return client + */ + public JenkinsHttpConnection getClient() { return client; } - public void setClient(JenkinsHttpClient client) { + /** + * Set the HTTP client. + * @param client {@link JenkinsHttpConnection}. + */ + public BaseModel setClient(final JenkinsHttpConnection client) { this.client = client; + return this; } + + protected static Predicate isBuildNumberEqualTo(int buildNumber) { + return build -> build.getNumber() == buildNumber; + } + } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java index 1d134a8b..470707d9 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Build.java @@ -8,15 +8,45 @@ import java.io.IOException; +import org.apache.http.HttpStatus; import org.apache.http.client.HttpResponseException; public class Build extends BaseModel { /** - * This will be returned by the API in cases where no build has ever - * been executed like {@link JobWithDetails#getLastBuild()} etc. + * This will be returned by the API in cases where no build has ever been + * executed like {@link JobWithDetails#getLastBuild()} etc. This will also + * be returned by {@link #details()} is the build has not been run. */ - public static final Build BUILD_HAS_NEVER_RAN = new Build (-1, -1, "UNKNOWN"); + public static final Build BUILD_HAS_NEVER_RUN = new Build(-1, -1, "UNKNOWN") { + @Override + public TestReport getTestReport() { + return TestReport.NO_TEST_REPORT; + } + + @Override + public BuildWithDetails details() { + // For a build which never has been run you couldn't get + // details about! + return BuildWithDetails.BUILD_HAS_NEVER_RUN; + } + }; + + /** + * This will be returned by the API in cases where a build has been + * cancelled. + */ + public static final Build BUILD_HAS_BEEN_CANCELLED = new Build(-1, -1, "CANCELLED") { + @Override + public TestReport getTestReport() { + return TestReport.NO_TEST_REPORT; + } + + @Override + public BuildWithDetails details() { + return BuildWithDetails.BUILD_HAS_BEEN_CANCELLED; + } + }; private int number; private int queueId; @@ -28,7 +58,7 @@ private Build(int number, int queueId, String url) { this.queueId = queueId; this.url = url; } - + public Build() { } @@ -53,10 +83,53 @@ public String getUrl() { return url; } + protected Build setNumber(int number) { + this.number = number; + return this; + } + + protected Build setQueueId(int queueId) { + this.queueId = queueId; + return this; + } + + protected Build setUrl(String url) { + this.url = url; + return this; + } + + /** + * + * @return The information from Jenkins. In cases the build has never run + * {@link #BUILD_HAS_NEVER_RUN} will be returned. + * @throws IOException + * in case of an error. + */ public BuildWithDetails details() throws IOException { return client.get(url, BuildWithDetails.class); } + /** + * This is to get the information about {@link TestReport} + * for a Maven Job type. + * @return {@link TestReport} + * @throws IOException in case of an error. + */ + public TestReport getTestReport() throws IOException { + return client.get(this.getUrl() + "/testReport/?depth=1", TestReport.class); + } + + /** + * This is to get the information about run tests for a + * non Maven job type. + * @return {@link TestResult} + * @throws IOException in case of an error. + */ + public TestResult getTestResult() throws IOException { + + return client.get(this.getUrl() + "/testReport/?depth=1", TestResult.class); + } + /* * This Change (Bad Practice) is due to inconsistencies in Jenkins various * versions. Jenkins changed their API from post to get and from get to post @@ -72,13 +145,40 @@ public String Stop() throws HttpResponseException, IOException { try { return client.get(url + "stop"); - } catch (IOException ex) { - if (((HttpResponseException) ex).getStatusCode() == 405) { + } catch (HttpResponseException ex) { + if (ex.getStatusCode() == HttpStatus.SC_METHOD_NOT_ALLOWED) { stopPost(); return ""; } + throw ex; } - return ""; + } + + /** Stops the build which is currently in progress. This version takes in + * a crumbFlag. In some cases , an error is thrown which reads + * "No valid crumb was included in the request". This stop method is used incase + * those issues occur + * + * @param crumbFlag flag used to specify if a crumb is passed into for the request + * @return the client url + * @throws HttpResponseException in case of an error. + * @throws IOException in case of an error. + */ + public String Stop(boolean crumbFlag) throws HttpResponseException, IOException { + try { + + return client.get(url + "stop"); + } catch (HttpResponseException ex) { + if (ex.getStatusCode() == HttpStatus.SC_METHOD_NOT_ALLOWED) { + stopPost(crumbFlag); + return ""; + } + throw ex; + } + } + + private void stopPost(boolean crumbFlag) throws HttpResponseException, IOException { + client.post(url + "stop", crumbFlag); } private void stopPost() throws HttpResponseException, IOException { @@ -86,26 +186,33 @@ private void stopPost() throws HttpResponseException, IOException { } @Override - public boolean equals(Object o) { - if (this == o) + public boolean equals(Object obj) { + if (this == obj) return true; - if (o == null || getClass() != o.getClass()) + if (obj == null) return false; - - Build build = (Build) o; - - if (number != build.number) + if (getClass() != obj.getClass()) return false; - if (url != null ? !url.equals(build.url) : build.url != null) + Build other = (Build) obj; + if (number != other.number) + return false; + if (queueId != other.queueId) + return false; + if (url == null) { + if (other.url != null) + return false; + } else if (!url.equals(other.url)) return false; - return true; } @Override public int hashCode() { - int result = number; - result = 31 * result + (url != null ? url.hashCode() : 0); + final int prime = 31; + int result = 1; + result = prime * result + number; + result = prime * result + queueId; + result = prime * result + ((url == null) ? 0 : url.hashCode()); return result; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildCause.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildCause.java index 6490b4a7..821a027f 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildCause.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildCause.java @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + package com.offbytwo.jenkins.model; public class BuildCause { @@ -14,54 +20,63 @@ public class BuildCause { public BuildCause() { this.upstreamBuild = new Integer(0); + //TODO: Think about initialization of the other + // attributes as well. + // userId = StringConstant.EMPTY; } public String getShortDescription() { return shortDescription; } - public void setShortDescription(String shortDescription) { + public BuildCause setShortDescription(String shortDescription) { this.shortDescription = shortDescription; + return this; } public int getUpstreamBuild() { return upstreamBuild; } - public void setUpstreamBuild(Integer upstreamBuild) { + public BuildCause setUpstreamBuild(Integer upstreamBuild) { this.upstreamBuild = upstreamBuild; + return this; } public String getUpstreamProject() { return upstreamProject; } - public void setUpstreamProject(String upstreamProject) { + public BuildCause setUpstreamProject(String upstreamProject) { this.upstreamProject = upstreamProject; + return this; } public String getUpstreamUrl() { return upstreamUrl; } - public void setUpstreamUrl(String upstreamUrl) { + public BuildCause setUpstreamUrl(String upstreamUrl) { this.upstreamUrl = upstreamUrl; + return this; } public String getUserId() { return userId; } - public void setUserId(String userId) { + public BuildCause setUserId(String userId) { this.userId = userId; + return this; } public String getUserName() { return userName; } - public void setUserName(String userName) { + public BuildCause setUserName(String userName) { this.userName = userName; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSet.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSet.java index cd199008..2b7cf520 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSet.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSet.java @@ -22,14 +22,15 @@ public class BuildChangeSet { private String kind; /** - * - */ - public void setItems(List items) { + * @param items {@link BuildChangeSet} + */ + public BuildChangeSet setItems(List items) { this.items = items; + return this; } /** - * @return the items + * @return list of {@link BuildChangeSet} */ public List getItems() { return items; @@ -43,10 +44,11 @@ public String getKind() { } /** - * - */ - public void setKind(String kind) { + * @param kind the kind of (usually svn, git). + */ + public BuildChangeSet setKind(String kind) { this.kind = kind; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetAuthor.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetAuthor.java index 9abdd1ba..af416581 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetAuthor.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetAuthor.java @@ -13,16 +13,18 @@ public String getAbsoluteUrl() { return absoluteUrl; } - public void setAbsoluteUrl(String absoluteUrl) { + public BuildChangeSetAuthor setAbsoluteUrl(String absoluteUrl) { this.absoluteUrl = absoluteUrl; + return this; } public String getFullName() { return fullName; } - public void setFullName(String fullName) { + public BuildChangeSetAuthor setFullName(String fullName) { this.fullName = fullName; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetItem.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetItem.java index e817d12b..922a19b1 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetItem.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetItem.java @@ -27,72 +27,81 @@ public List getAffectedPaths() { return affectedPaths; } - public void setAffectedPaths(List affectedPaths) { + public BuildChangeSetItem setAffectedPaths(List affectedPaths) { this.affectedPaths = affectedPaths; + return this; } public String getCommitId() { return commitId; } - public void setCommitId(String commitId) { + public BuildChangeSetItem setCommitId(String commitId) { this.commitId = commitId; + return this; } public String getTimestamp() { return timestamp; } - public void setTimestamp(String timeStamp) { + public BuildChangeSetItem setTimestamp(String timeStamp) { this.timestamp = timeStamp; + return this; } public String getComment() { return comment; } - public void setComment(String comment) { + public BuildChangeSetItem setComment(String comment) { this.comment = comment; + return this; } public String getDate() { return date; } - public void setDate(String date) { + public BuildChangeSetItem setDate(String date) { this.date = date; + return this; } public String getId() { return id; } - public void setId(String id) { + public BuildChangeSetItem setId(String id) { this.id = id; + return this; } public String getMsg() { return msg; } - public void setMsg(String msg) { + public BuildChangeSetItem setMsg(String msg) { this.msg = msg; + return this; } public List getPaths() { return paths; } - public void setPaths(List paths) { + public BuildChangeSetItem setPaths(List paths) { this.paths = paths; + return this; } public BuildChangeSetAuthor getAuthor() { return author; } - public void setAuthor(BuildChangeSetAuthor author) { + public BuildChangeSetItem setAuthor(BuildChangeSetAuthor author) { this.author = author; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetPath.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetPath.java index 4bd1a055..5758ee78 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetPath.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildChangeSetPath.java @@ -7,26 +7,38 @@ public class BuildChangeSetPath { /** - * Usually {@code edit} etc. + * The SCM operation, add or edit or delete + * @see EditType */ - // TODO: Think about it if its possible to use an enum type? - private String editType; // edit, ? + private String editType; private String file; + /** + * Return the SCM operation. + * @return the SCM operation, add or edit or delete + * @see EditType + */ public String getEditType() { return editType; } - public void setEditType(String editType) { + /** + * Sets the SCM operation. + * @param editType the SCM operation, add or edit or delete + * @see EditType + */ + public BuildChangeSetPath setEditType(String editType) { this.editType = editType; + return this; } public String getFile() { return file; } - public void setFile(String file) { + public BuildChangeSetPath setFile(String file) { this.file = file; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildResult.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildResult.java index e4aad42f..64b695c6 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildResult.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildResult.java @@ -7,5 +7,26 @@ package com.offbytwo.jenkins.model; public enum BuildResult { - FAILURE, UNSTABLE, REBUILDING, BUILDING, ABORTED, SUCCESS, UNKNOWN, NOT_BUILT + FAILURE, UNSTABLE, REBUILDING, BUILDING, + /** + * This means a job was already running and has been aborted. + */ + ABORTED, + /** + * + */ + SUCCESS, + /** + * ? + */ + UNKNOWN, + /** + * This is returned if a job has never been built. + */ + NOT_BUILT, + /** + * This will be the result of a job in cases where it has been cancelled + * during the time in the queue. + */ + CANCELLED } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java index f9ec3c25..d3ce5842 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/BuildWithDetails.java @@ -6,33 +6,151 @@ package com.offbytwo.jenkins.model; -import com.google.common.base.Predicate; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.offbytwo.jenkins.helper.BuildConsoleStreamListener; +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toMap; + +/** + * This class represents build information with details about what has been done + * like duration start and of course the build result. + * + */ +public class BuildWithDetails extends Build { -import static com.google.common.collect.Collections2.filter; + private final Logger LOGGER = LoggerFactory.getLogger(getClass()); -public class BuildWithDetails extends Build { + public final static String TEXT_SIZE_HEADER = "x-text-size"; + public final static String MORE_DATA_HEADER = "x-more-data"; + + /** + * This will be returned by the API in cases where the build has never run. + * For example {@link Build#BUILD_HAS_NEVER_RUN} + */ + public static final BuildWithDetails BUILD_HAS_NEVER_RUN = new BuildWithDetails() { + + @Override + public List getActions() { + return Collections.emptyList(); + } + + @Override + public List getArtifacts() { + return Collections.emptyList(); + } + + @Override + public List getCauses() { + return Collections.emptyList(); + } + + @Override + public List getCulprits() { + return Collections.emptyList(); + } - List actions; // Should be improved. - boolean building; - String description; - long duration; - long estimatedDuration; - String fullDisplayName; - String id; - long timestamp; - BuildResult result; - List artifacts; - String consoleOutputText; - String consoleOutputHtml; - BuildChangeSet changeSet; - String builtOn; - List culprits; + @Override + public BuildResult getResult() { + return BuildResult.NOT_BUILT; + } + + }; + + /** + * This will be returned by the API in cases where the build has been + * cancelled. For example {@link Build#BUILD_HAS_BEEN_CANCELLED} + */ + public static final BuildWithDetails BUILD_HAS_BEEN_CANCELLED = new BuildWithDetails() { + + @Override + public List getActions() { + return Collections.emptyList(); + } + + @Override + public List getArtifacts() { + return Collections.emptyList(); + } + + @Override + public List getCauses() { + return Collections.emptyList(); + } + + @Override + public List getCulprits() { + return Collections.emptyList(); + } + + @Override + public BuildResult getResult() { + return BuildResult.CANCELLED; + } + + }; + + private List>>> actions; // TODO: Should be improved. + private boolean building; + private String description; + private String displayName; + private long duration; + private long estimatedDuration; + private String fullDisplayName; + private String id; + private long timestamp; + private BuildResult result; + private List artifacts; + private String consoleOutputText; + private String consoleOutputHtml; + private BuildChangeSet changeSet; + @JsonProperty("changeSets") + private List changeSets; + private String builtOn; + private List culprits; + + public BuildWithDetails() { + // Default ctor is needed to jackson. + } + + public BuildWithDetails(BuildWithDetails details) { + this.actions = details.actions; + this.description = details.description; + this.displayName = details.displayName; + this.building = details.building; + this.duration = details.duration; + this.estimatedDuration = details.estimatedDuration; + this.fullDisplayName = details.fullDisplayName; + this.id = details.id; + this.timestamp = details.timestamp; + this.result = details.result; + this.artifacts = details.artifacts; + this.consoleOutputHtml = details.consoleOutputHtml; + this.consoleOutputText = details.consoleOutputText; + this.changeSet = details.changeSet; + this.builtOn = details.builtOn; + this.culprits = details.culprits; + this.setClient(details.getClient()); + } public List getArtifacts() { return artifacts; @@ -43,37 +161,149 @@ public boolean isBuilding() { } public List getCauses() { - // actions is a List List[BuildCause] - Collection causes = filter(actions, new Predicate>() { - @Override - public boolean apply(Map action) { - return action.containsKey("causes"); - } - }); - - List result = new ArrayList(); - - if (causes != null && !causes.isEmpty()) { - // The underlying key-value can be either a or a - // . - List> causes_blob = ((Map>>) causes.toArray()[0]) - .get("causes"); - for (Map cause : causes_blob) { - - BuildCause cause_object = new BuildCause(); - cause_object.setShortDescription((String) cause.get("shortDescription")); - cause_object.setUpstreamBuild((Integer) cause.get("upstreamBuild")); - cause_object.setUpstreamProject((String) cause.get("upstreamProject")); - cause_object.setUpstreamUrl((String) cause.get("upstreamUrl")); - cause_object.setUserId((String) cause.get("userId")); - cause_object.setUserName((String) cause.get("userName")); - - result.add(cause_object); - } + return actions.stream() + .filter(item -> item.containsKey("causes")) + .flatMap(item -> item.entrySet().stream()) + .flatMap(sub -> sub.getValue().stream()) + .map(item -> convertToBuildCause(item)) + .collect(toList()); + } + + /** + * Update displayName and the description of a + * build. + * + * @param displayName The new displayName which should be set. + * @param description The description which should be set. + * @param crumbFlag true or false. + * @throws IOException in case of errors. + */ + public BuildWithDetails updateDisplayNameAndDescription(String displayName, String description, boolean crumbFlag) + throws IOException { + Objects.requireNonNull(displayName, "displayName is not allowed to be null."); + Objects.requireNonNull(description, "description is not allowed to be null."); + //TODO:JDK9+ Map.of()... + Map params = new HashMap<>(); + params.put("displayName", displayName); + params.put("description", description); + // TODO: Check what the "core:apply" means? + params.put("core:apply", ""); + params.put("Submit", "Save"); + client.post_form(this.getUrl() + "/configSubmit?", params, crumbFlag); + return this; + } + + /** + * Update displayName and the description of a + * build. + * + * @param displayName The new displayName which should be set. + * @param description The description which should be set. + * @throws IOException in case of errors. + */ + public BuildWithDetails updateDisplayNameAndDescription(String displayName, String description) throws IOException { + return updateDisplayNameAndDescription(displayName, description, false); + } + + /** + * Update displayName of a build. + * + * @param displayName The new displayName which should be set. + * @param crumbFlag true or false. + * @throws IOException in case of errors. + */ + public BuildWithDetails updateDisplayName(String displayName, boolean crumbFlag) throws IOException { + Objects.requireNonNull(displayName, "displayName is not allowed to be null."); + String description = getDescription() == null ? "" : getDescription(); + Map params = new HashMap<>(); + params.put("displayName", displayName); + params.put("description", description); + // TODO: Check what the "core:apply" means? + params.put("core:apply", ""); + params.put("Submit", "Save"); + client.post_form(this.getUrl() + "/configSubmit?", params, crumbFlag); + return this; + } + + /** + * Update displayName of a build. + * + * @param displayName The new displayName which should be set. + * @throws IOException in case of errors. + */ + public BuildWithDetails updateDisplayName(String displayName) throws IOException { + return updateDisplayName(displayName, false); + } + + /** + * Update the description of a build. + * + * @param description The description which should be set. + * @param crumbFlag true or false. + * @throws IOException in case of errors. + */ + public BuildWithDetails updateDescription(String description, boolean crumbFlag) throws IOException { + Objects.requireNonNull(description, "description is not allowed to be null."); + String displayName = getDisplayName() == null ? "" : getDisplayName(); + //JDK9+: Map.of(..) + Map params = new HashMap<>(); + params.put("displayName", displayName); + params.put("description", description); + // TODO: Check what the "core:apply" means? + params.put("core:apply", ""); + params.put("Submit", "Save"); + client.post_form(this.getUrl() + "/configSubmit?", params, crumbFlag); + return this; + } + + /** + * Update the description of a build. + * + * @param description The description which should be set. + * @throws IOException in case of errors. + */ + public BuildWithDetails updateDescription(String description) throws IOException { + return updateDescription(description, false); + } + + private boolean isNullOrEmpty(String value) { + return value == null || value.isEmpty(); + } + + private BuildCause convertToBuildCause(Map cause) { + BuildCause cause_object = new BuildCause(); + + // TODO: Think about it. Can this be done more simpler? + String description = (String) cause.get("shortDescription"); + if (!isNullOrEmpty(description)) { + cause_object.setShortDescription(description); } - return result; + Integer upstreamBuild = (Integer) cause.get("upstreamBuild"); + if (upstreamBuild != null) { + cause_object.setUpstreamBuild(upstreamBuild); + } + + String upstreamProject = (String) cause.get("upstreamProject"); + if (!isNullOrEmpty(upstreamProject)) { + cause_object.setUpstreamProject(upstreamProject); + } + + String upstreamUrl = (String) cause.get("upstreamUrl"); + if (!isNullOrEmpty(upstreamUrl)) { + cause_object.setUpstreamUrl(upstreamUrl); + } + + String userId = (String) cause.get("userId"); + if (!isNullOrEmpty(userId)) { + cause_object.setUserId(userId); + } + + String userName = (String) cause.get("userName"); + if (!isNullOrEmpty(userName)) { + cause_object.setUserName(userName); + } + return cause_object; } public String getDescription() { @@ -92,6 +322,10 @@ public String getFullDisplayName() { return fullDisplayName; } + public String getDisplayName() { + return displayName; + } + public String getId() { return id; } @@ -112,68 +346,204 @@ public List getActions() { return actions; } - public Map getParameters() { - Collection parameters = filter(actions, new Predicate>() { - @Override - public boolean apply(Map action) { - return action.containsKey("parameters"); - } - }); - - Map params = new HashMap(); - - if (parameters != null && !parameters.isEmpty()) { - for (Map param : ((Map>>) parameters.toArray()[0]) - .get("parameters")) { - String key = (String) param.get("name"); - Object value = param.get("value"); - params.put(key, String.valueOf(value)); - } - } + public Map getParameters() { + Map parameters = actions.stream() + .filter(item -> item.containsKey("parameters")) + .flatMap(item -> item.entrySet().stream()) + .flatMap(sub -> sub.getValue().stream()) + .collect(toMap(k -> (String) k.get("name"), v -> v.get("value"))); - return params; + return parameters; } /** - * @return The console output of the build. The line separation is done by + * @return The full console output of the build. The line separation is done by * {@code CR+LF}. - * @throws IOException - * in case of a failure. + * + * @see #streamConsoleOutput(BuildConsoleStreamListener, int, int, boolean) method for obtaining logs for running build + * + * @throws IOException in case of a failure. */ public String getConsoleOutputText() throws IOException { return client.get(getUrl() + "/logText/progressiveText"); } + /** + * The full console output with HTML. + * + * @see #streamConsoleOutput(BuildConsoleStreamListener, int, int, boolean) method for obtaining logs for running build + * + * @return The console output as HTML. + * @throws IOException in case of an error. + */ public String getConsoleOutputHtml() throws IOException { return client.get(getUrl() + "/logText/progressiveHtml"); } + + /** + * Stream build console output log as text using BuildConsoleStreamListener + * Method can be used to asynchronously obtain logs for running build. + * + * @param listener interface used to asynchronously obtain logs + * @param poolingInterval interval (seconds) used to pool jenkins for logs + * @param poolingTimeout pooling timeout (seconds) used to break pooling in case build stuck + * @throws InterruptedException in case of an error. + * @throws IOException in case of an error. + * + */ + public void streamConsoleOutput(final BuildConsoleStreamListener listener, final int poolingInterval, final int poolingTimeout, boolean crumbFlag) throws InterruptedException, IOException { + // Calculate start and timeout + final long startTime = System.currentTimeMillis(); + final long timeoutTime = startTime + (poolingTimeout * 1000); + + int bufferOffset = 0; + while (true) { + Thread.sleep(poolingInterval * 1000); + + ConsoleLog consoleLog = null; + consoleLog = getConsoleOutputText(bufferOffset, crumbFlag); + String logString = consoleLog.getConsoleLog(); + if (logString != null && !logString.isEmpty()) { + listener.onData(logString); + } + if (consoleLog.getHasMoreData()) { + bufferOffset = consoleLog.getCurrentBufferSize(); + } else { + listener.finished(); + break; + } + long currentTime = System.currentTimeMillis(); + + if (currentTime > timeoutTime) { + LOGGER.warn("Pooling for build {0} for {2} timeout! Check if job stuck in jenkins", + BuildWithDetails.this.getDisplayName(), BuildWithDetails.this.getNumber()); + break; + } + } + } + + /** + * Get build console output log as text. + * Use this method to periodically obtain logs from jenkins and skip chunks that were already received + * + * @param bufferOffset offset in console lo + * @param crumbFlag true or false. + * @return ConsoleLog object containing console output of the build. The line separation is done by + * {@code CR+LF}. + * @throws IOException in case of a failure. + */ + public ConsoleLog getConsoleOutputText(int bufferOffset, boolean crumbFlag) throws IOException { + List formData = new ArrayList<>(); + formData.add(new BasicNameValuePair("start", Integer.toString(bufferOffset))); + String path = getUrl() + "logText/progressiveText"; + HttpResponse httpResponse = client.post_form_with_result(path, formData, crumbFlag); + + Header moreDataHeader = httpResponse.getFirstHeader(MORE_DATA_HEADER); + Header textSizeHeader = httpResponse.getFirstHeader(TEXT_SIZE_HEADER); + String response = EntityUtils.toString(httpResponse.getEntity()); + boolean hasMoreData = false; + if (moreDataHeader != null) { + hasMoreData = Boolean.TRUE.toString().equals(moreDataHeader.getValue()); + } + Integer currentBufferSize = bufferOffset; + if (textSizeHeader != null) { + try { + currentBufferSize = Integer.parseInt(textSizeHeader.getValue()); + } catch (NumberFormatException e) { + LOGGER.warn("Cannot parse buffer size for job {0} build {1}. Using current offset!", this.getDisplayName(), this.getNumber()); + } + } + return new ConsoleLog(response, hasMoreData, currentBufferSize); + } + + + /** + * Returns the change set of a build if available. + * + * If a build performs several scm checkouts (i.e. pipeline builds), the change set of the first + * checkout is returned. To get the complete list of change sets for all checkouts, use + * {@link #getChangeSets()} + * + * If no checkout is performed, null is returned. + * + * @return The change set of the build. + * + */ public BuildChangeSet getChangeSet() { - return changeSet; + BuildChangeSet result; + if (changeSet != null) { + result = changeSet; + } else if (changeSets != null && !changeSets.isEmpty()) { + result = changeSets.get(0); + } else { + result = null; + } + return result; } - public void setChangeSet(BuildChangeSet changeSet) { + public BuildWithDetails setChangeSet(BuildChangeSet changeSet) { this.changeSet = changeSet; + return this; + } + + /** + * Returns the complete list of change sets for all checkout the build has performed. If no + * checkouts have been performed, returns null. + * + * @return The complete list of change sets of the build. + */ + public List getChangeSets() { + List result; + if (changeSets != null) { + result = changeSets; + } else if (changeSet != null) { + result = Collections.singletonList(changeSet); + } else { + result = null; + } + return result; + } + + public BuildWithDetails setChangeSets(List changeSets) { + this.changeSets = changeSets; + return this; } public List getCulprits() { return culprits; } - public void setCulprits(List culprits) { + public BuildWithDetails setCulprits(List culprits) { this.culprits = culprits; + return this; + } + + public BuildWithDetails setResult(BuildResult result) { + this.result = result; + return this; } public InputStream downloadArtifact(Artifact a) throws IOException, URISyntaxException { // We can't just put the artifact's relative path at the end of the url - // string, - // as there could be characters that need to be escaped. + // string, as there could be characters that need to be escaped. URI uri = new URI(getUrl()); String artifactPath = uri.getPath() + "artifact/" + a.getRelativePath(); URI artifactUri = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), artifactPath, "", ""); return client.getFile(artifactUri); } + + /** + * Returns {@link MavenModuleWithDetails} based on its name + * + * @param name module name + * @return {@link MavenModuleWithDetails} + * @throws IOException in case of error. + */ + public MavenModuleWithDetails getModule(String name) throws IOException { + return client.get(getUrl() + name, MavenModuleWithDetails.class); + } @Override public boolean equals(Object obj) { @@ -206,6 +576,11 @@ public boolean equals(Object obj) { return false; } else if (!changeSet.equals(other.changeSet)) return false; + if (changeSets == null) { + if (other.changeSets != null) + return false; + } else if (!changeSets.equals(other.changeSets)) + return false; if (consoleOutputHtml == null) { if (other.consoleOutputHtml != null) return false; @@ -226,6 +601,11 @@ public boolean equals(Object obj) { return false; } else if (!description.equals(other.description)) return false; + if (displayName == null) { + if (other.displayName != null) + return false; + } else if (!displayName.equals(other.displayName)) + return false; if (duration != other.duration) return false; if (estimatedDuration != other.estimatedDuration) @@ -256,10 +636,12 @@ public int hashCode() { result = prime * result + (building ? 1231 : 1237); result = prime * result + ((builtOn == null) ? 0 : builtOn.hashCode()); result = prime * result + ((changeSet == null) ? 0 : changeSet.hashCode()); + result = prime * result + ((changeSets == null) ? 0 : changeSets.hashCode()); result = prime * result + ((consoleOutputHtml == null) ? 0 : consoleOutputHtml.hashCode()); result = prime * result + ((consoleOutputText == null) ? 0 : consoleOutputText.hashCode()); result = prime * result + ((culprits == null) ? 0 : culprits.hashCode()); result = prime * result + ((description == null) ? 0 : description.hashCode()); + result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); result = prime * result + (int) (duration ^ (duration >>> 32)); result = prime * result + (int) (estimatedDuration ^ (estimatedDuration >>> 32)); result = prime * result + ((fullDisplayName == null) ? 0 : fullDisplayName.hashCode()); diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/CauseAction.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/CauseAction.java new file mode 100644 index 00000000..dd455b50 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/CauseAction.java @@ -0,0 +1,72 @@ +package com.offbytwo.jenkins.model; + +public class CauseAction extends BaseModel { + private String shortDescription; + private String userId; + private String userName; + + public String getShortDescription() { + return shortDescription; + } + + public CauseAction setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + return this; + } + + public String getUserId() { + return userId; + } + + public CauseAction setUserId(String userId) { + this.userId = userId; + return this; + } + + public String getUserName() { + return userName; + } + + public CauseAction setUserName(String userName) { + this.userName = userName; + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((shortDescription == null) ? 0 : shortDescription.hashCode()); + result = prime * result + ((userId == null) ? 0 : userId.hashCode()); + result = prime * result + ((userName == null) ? 0 : userName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + CauseAction other = (CauseAction) obj; + if (shortDescription == null) { + if (other.shortDescription != null) + return false; + } else if (!shortDescription.equals(other.shortDescription)) + return false; + if (userId == null) { + if (other.userId != null) + return false; + } else if (!userId.equals(other.userId)) + return false; + if (userName == null) { + if (other.userName != null) + return false; + } else if (!userName.equals(other.userName)) + return false; + return true; + } + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java index ae0232ef..2414f061 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Computer.java @@ -9,23 +9,31 @@ import java.io.IOException; import java.util.List; -import com.google.common.net.UrlEscapers; +import com.offbytwo.jenkins.client.util.EncodingUtils; +import com.fasterxml.jackson.annotation.JsonProperty; +/** + * @author Kelly Plummer + * + */ public class Computer extends BaseModel { private String displayName; - public List getComputers() { - return computer; - } + private List computers; - public void setComputer(List computer) { - this.computer = computer; + public Computer() { } - List computer; + @JsonProperty("computer") + public List getComputers() { + return computers; + } - public Computer() { + @JsonProperty("computer") + public Computer setComputers(List computers) { + this.computers = computers; + return this; } public Computer(String displayName) { @@ -42,7 +50,7 @@ public ComputerWithDetails details() throws IOException { if ("master".equals(displayName)) { name = "(master)"; } else { - name = UrlEscapers.urlPathSegmentEscaper().escape(displayName); + name = EncodingUtils.encode(displayName); } // TODO: Check if depth=2 is a good idea or if it could be solved // better. @@ -61,10 +69,10 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; Computer other = (Computer) obj; - if (computer == null) { - if (other.computer != null) + if (computers == null) { + if (other.computers != null) return false; - } else if (!computer.equals(other.computer)) + } else if (!computers.equals(other.computers)) return false; if (displayName == null) { if (other.displayName != null) @@ -78,7 +86,7 @@ public boolean equals(Object obj) { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((computer == null) ? 0 : computer.hashCode()); + result = prime * result + ((computers == null) ? 0 : computers.hashCode()); result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); return result; } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerLabel.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerLabel.java new file mode 100644 index 00000000..e61ecd2d --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerLabel.java @@ -0,0 +1,34 @@ +package com.offbytwo.jenkins.model; + +public class ComputerLabel { + private String name; + + public String getName() { + return name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((name == null) ? 0 : name.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ComputerLabel other = (ComputerLabel) obj; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + return true; + } +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerSet.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerSet.java index 4eddf182..0c12ba5a 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerSet.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerSet.java @@ -1,11 +1,13 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + package com.offbytwo.jenkins.model; import java.util.List; - -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import java.util.stream.Collectors; public class ComputerSet extends BaseModel { private int busyExecutors; @@ -22,38 +24,39 @@ public int getBusyExecutors() { return busyExecutors; } - public void setBusyExecutors(int busyExecutors) { + public ComputerSet setBusyExecutors(int busyExecutors) { this.busyExecutors = busyExecutors; + return this; } public String getDisplayName() { return displayName; } - public void setDisplayName(String displayName) { + public ComputerSet setDisplayName(String displayName) { this.displayName = displayName; + return this; } public int getTotalExecutors() { return totalExecutors; } - public void setTotalExecutors(int totalExecutors) { + public ComputerSet setTotalExecutors(int totalExecutors) { this.totalExecutors = totalExecutors; + return this; } - public List getComputer() { - return Lists.transform( computer, new Function() { - @Override - public ComputerWithDetails apply(ComputerWithDetails computerWithDetails) { - computerWithDetails.setClient(client); - return computerWithDetails; - } - }); + public List getComputers() { + return computer.stream().map(s -> { + s.setClient(this.client); + return s; + }).collect(Collectors.toList()); } - public void setComputer(List computers) { + public ComputerSet setComputer(List computers) { this.computer = computers; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerWithDetails.java index 06236e5f..c57b40a5 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerWithDetails.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ComputerWithDetails.java @@ -10,30 +10,31 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import com.google.common.base.Function; -import com.google.common.net.UrlEscapers; +import com.offbytwo.jenkins.client.util.EncodingUtils; +import java.util.function.Function; public class ComputerWithDetails extends Computer { - String displayName; - List actions; - List executors; - Boolean idle; - Boolean jnlp; - Boolean launchSupported; - Boolean manualLaunchAllowed; - Map monitorData; - Integer numExecutors; - Boolean offline; - OfflineCause offlineCause; - String offlineCauseReason; - List oneOffExecutors; - Boolean temporarilyOffline; + private String displayName; + private List actions; //TODO: What kind of List? + private List executors; + private List assignedLabels; + private Boolean idle; + private Boolean jnlp; + private Boolean launchSupported; + private Boolean manualLaunchAllowed; + private Map monitorData; //TODO: What kind of map? + private Integer numExecutors; + private Boolean offline; + private OfflineCause offlineCause; + private String offlineCauseReason; + private List oneOffExecutors; //TODO: What kind of List? + private Boolean temporarilyOffline; public ComputerWithDetails() { } + @Override public String getDisplayName() { return displayName; } @@ -46,6 +47,10 @@ public List getExecutors() { return executors; } + public List getAssignedLabels() { + return assignedLabels; + } + public Boolean getIdle() { return idle; } @@ -63,7 +68,7 @@ public Boolean getLaunchSupported() { * (node) name. * * @return {@link LoadStatistics} - * @throws IOException + * @throws IOException in case of an error. */ public LoadStatistics getLoadStatistics() throws IOException { // TODO: Think about the following handling, cause that has also being @@ -72,29 +77,45 @@ public LoadStatistics getLoadStatistics() throws IOException { if ("master".equals(displayName)) { name = "(master)"; } else { - name = UrlEscapers.urlPathSegmentEscaper().escape(displayName); + name = EncodingUtils.encode(displayName); } // TODO: ?depth=2 good idea or could this being done better? return client.get("/computer/" + name + "/" + "loadStatistics/?depth=2", LoadStatistics.class); } - public void toggleOffline(boolean crumbFlag) throws IOException { + public ComputerWithDetails toggleOffline(boolean crumbFlag) throws IOException { // curl --data "json=init" -X POST "http://192.168.99.100:8080/computer/(master)/toggleOffline" String name; if ("master".equals(displayName)) { name = "(master)"; } else { - name = UrlEscapers.urlPathSegmentEscaper().escape(displayName); + name = EncodingUtils.encode(displayName); } - Map data = new HashMap(); - data.put( "json", "init" ); client.post( "/computer/" + name + "/toggleOffline", crumbFlag); + return this; + } + + public ComputerWithDetails toggleOffline() throws IOException { + return toggleOffline(false); } - public void toggleOffline() throws IOException { - toggleOffline( false ); + public ComputerWithDetails changeOfflineCause(String cause, boolean crumbFlag) throws IOException { + String name; + if ("master".equals(displayName)) { + name = "(master)"; + } else { + name = EncodingUtils.encode(displayName); + } + Map data = new HashMap(); + data.put("offlineMessage", cause); + client.post_form("/computer/" + name + "/changeOfflineCause?", data, crumbFlag); + return this; + } + + public ComputerWithDetails changeOfflineCause(String cause) throws IOException { + return changeOfflineCause(cause, false); } public Boolean getManualLaunchAllowed() { @@ -153,6 +174,11 @@ public boolean equals(Object obj) { return false; } else if (!executors.equals(other.executors)) return false; + if (assignedLabels == null) { + if (other.assignedLabels != null) + return false; + } else if (!assignedLabels.equals(other.assignedLabels)) + return false; if (idle == null) { if (other.idle != null) return false; @@ -232,11 +258,4 @@ public int hashCode() { return result; } - private class ComputerWithClient implements Function { - @Override - public Computer apply(Computer computer) { - computer.setClient(client); - return computer; - } - } } \ No newline at end of file diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ConsoleLog.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ConsoleLog.java new file mode 100644 index 00000000..503e1ce0 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ConsoleLog.java @@ -0,0 +1,44 @@ +package com.offbytwo.jenkins.model; + +/** + * Represents build console log + */ +public class ConsoleLog { + + private String consoleLog; + private Boolean hasMoreData; + private Integer currentBufferSize; + + public ConsoleLog(String consoleLog, Boolean hasMoreData, Integer currentBufferSize) { + this.consoleLog = consoleLog; + this.hasMoreData = hasMoreData; + this.currentBufferSize = currentBufferSize; + } + + public String getConsoleLog() { + return consoleLog; + } + + public ConsoleLog setConsoleLog(String consoleLog) { + this.consoleLog = consoleLog; + return this; + } + + public Boolean getHasMoreData() { + return hasMoreData; + } + + public ConsoleLog setHasMoreData(Boolean hasMoreData) { + this.hasMoreData = hasMoreData; + return this; + } + + public Integer getCurrentBufferSize() { + return currentBufferSize; + } + + public ConsoleLog setCurrentBufferSize(Integer currentBufferSize) { + this.currentBufferSize = currentBufferSize; + return this; + } +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Crumb.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Crumb.java index 95dfc8de..b79d895b 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Crumb.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Crumb.java @@ -1,7 +1,9 @@ package com.offbytwo.jenkins.model; /** - * @author Adrien Lecharpentier + * @author Adrien Lecharpentier + * adrien. + * lecharpentier@gmail.com */ public class Crumb extends BaseModel { diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executable.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executable.java index 8722d524..5f044e13 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executable.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executable.java @@ -1,24 +1,26 @@ package com.offbytwo.jenkins.model; public class Executable { - Long number; + private Long number; - String url; + private String url; public Long getNumber() { return number; } - public void setNumber(Long number) { + public Executable setNumber(Long number) { this.number = number; + return this; } public String getUrl() { return url; } - public void setUrl(String url) { + public Executable setUrl(String url) { this.url = url; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executor.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executor.java index b6e2c071..c6914d97 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executor.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Executor.java @@ -2,67 +2,75 @@ /** * This class is based on informations which can be extracted from an running - * Jenkins instance like this: {@link http://jenkins-server/computer/api/schema} + * Jenkins instance via + * http://jenkins-server/ + * computer/api/schema. * * @author Karl Heinz Marbaise * */ public class Executor { - private String currentExecutable; + private Job currentExecutable; // in XSD it's a reference to a class - private String currentWorkUnit; + private Job currentWorkUnit; private Boolean idle; private Boolean likelyStuck; private int number; private int progress; - public String getCurrentExecutable() { + public Job getCurrentExecutable() { return currentExecutable; } - public void setCurrentExecutable(String currentExecutable) { + public Executor setCurrentExecutable(Job currentExecutable) { this.currentExecutable = currentExecutable; + return this; } - public String getCurrentWorkUnit() { + public Job getCurrentWorkUnit() { return currentWorkUnit; } - public void setCurrentWorkUnit(String currentWorkUnit) { + public Executor setCurrentWorkUnit(Job currentWorkUnit) { this.currentWorkUnit = currentWorkUnit; + return this; } public Boolean getIdle() { return idle; } - public void setIdle(Boolean idle) { + public Executor setIdle(Boolean idle) { this.idle = idle; + return this; } public Boolean getLikelyStuck() { return likelyStuck; } - public void setLikelyStuck(Boolean likelyStuck) { + public Executor setLikelyStuck(Boolean likelyStuck) { this.likelyStuck = likelyStuck; + return this; } public int getNumber() { return number; } - public void setNumber(int number) { + public Executor setNumber(int number) { this.number = number; + return this; } public int getProgress() { return progress; } - public void setProgress(int progress) { + public Executor setProgress(int progress) { this.progress = progress; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ExtractHeader.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ExtractHeader.java index cb3c683a..6dd43221 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ExtractHeader.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ExtractHeader.java @@ -4,8 +4,9 @@ public class ExtractHeader extends BaseModel { private String location; - public void setLocation(String value) { + public ExtractHeader setLocation(String value) { location = value; + return this; } public String getLocation() { diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/FolderJob.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/FolderJob.java index 10b9dbdb..f2677081 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/FolderJob.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/FolderJob.java @@ -1,18 +1,20 @@ package com.offbytwo.jenkins.model; +import com.offbytwo.jenkins.helper.FunctionalHelper; + import java.io.IOException; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; -import com.google.common.base.Function; -import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Maps; -import com.offbytwo.jenkins.client.util.EncodingUtils; +import static com.offbytwo.jenkins.helper.FunctionalHelper.SET_CLIENT; public class FolderJob extends Job { - String displayName; - List jobs; + private String displayName; + private List jobs; public FolderJob() { } @@ -20,6 +22,10 @@ public FolderJob() { public FolderJob(String name, String url) { super(name, url); } + + public FolderJob(String name, String url, String fullName) { + super(name, url, fullName); + } public String getDisplayName() { return displayName; @@ -42,55 +48,60 @@ public boolean isFolder() { /** * Get a list of all the defined jobs in this folder * - * @return list of defined jobs (summary level, for details @see Job#details - * @throws IOException + * @return list of defined jobs (summary level, for details {@link Job#details()}. */ public Map getJobs() { - return Maps.uniqueIndex(jobs, new Function() { - @Override - public String apply(Job job) { - job.setClient(client); - return job.getName(); - } - }); + //FIXME: Check for null of jobs? Can that happen? + return jobs.stream() + .map(SET_CLIENT(this.client)) + .collect(Collectors.toMap(k -> k.getName(), Function.identity())); } /** * Get a job in this folder by name * + * @param name the name of the job. * @return the given job - * @throws IOException + * @throws IllegalArgumentException in case if the {@code name} does not exist. */ public Job getJob(String name) { - return Maps.uniqueIndex(jobs, new Function() { - @Override - public String apply(Job job) { - job.setClient(client); - return job.getName(); - } - }).get(name); + //FIXME: Check for null of jobs? Can that happen? + return jobs.stream() + .map(SET_CLIENT(this.client)) + .filter(item -> item.getName().equals(name)) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("Job with name " + name + " does not exist.")); } /** * Create a folder on the server (as a subfolder of this folder) * - * @throws IOException + * @param folderName name of the folder to be created. + * @throws IOException in case of an error. */ - public void createFolder(String jobName) throws IOException { - createFolder(jobName, false); + public FolderJob createFolder(String folderName) throws IOException { + return createFolder(folderName, false); } /** * Create a folder on the server (as a subfolder of this folder) * - * @throws IOException + * @param folderName name of the folder to be created. + * @param crumbFlag true/false. + * @return + * @throws IOException in case of an error. */ - public void createFolder(String jobName, Boolean crumbFlag) throws IOException { + public FolderJob createFolder(String folderName, Boolean crumbFlag) throws IOException { // https://gist.github.com/stuart-warren/7786892 was slightly helpful // here - ImmutableMap params = ImmutableMap.of("mode", "com.cloudbees.hudson.plugins.folder.Folder", - "name", EncodingUtils.encodeParam(jobName), "from", "", "Submit", "OK"); + //TODO: JDK9+: Map.of(...) + Map params = new HashMap<>(); + params.put("mode", "com.cloudbees.hudson.plugins.folder.Folder"); + params.put("name", folderName); + params.put("from", ""); + params.put("Submit", "OK"); client.post_form(this.getUrl() + "/createItem?", params, crumbFlag); + return this; } /* diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/HourMinSec10.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/HourMinSec10.java index 0dba01dc..66823616 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/HourMinSec10.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/HourMinSec10.java @@ -15,24 +15,27 @@ public Statis getHour() { return hour; } - public void setHour(Statis hour) { + public HourMinSec10 setHour(Statis hour) { this.hour = hour; + return this; } public Statis getMin() { return min; } - public void setMin(Statis min) { + public HourMinSec10 setMin(Statis min) { this.min = min; + return this; } public Statis getSec10() { return sec10; } - public void setSec10(Statis sec10) { + public HourMinSec10 setSec10(Statis sec10) { this.sec10 = sec10; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JacocoCoverageReport.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JacocoCoverageReport.java new file mode 100644 index 00000000..cc1345d5 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JacocoCoverageReport.java @@ -0,0 +1,56 @@ +package com.offbytwo.jenkins.model; + +public class JacocoCoverageReport extends BaseModel { + + private JacocoCoverageResult lineCoverage; + private JacocoCoverageResult classCoverage; + private JacocoCoverageResult complexityScore; + private JacocoCoverageResult instructionCoverage; + private JacocoCoverageResult branchCoverage; + + public JacocoCoverageResult getLineCoverage() { + return lineCoverage; + } + + public JacocoCoverageReport setLineCoverage(JacocoCoverageResult lineCoverage) { + this.lineCoverage = lineCoverage; + return this; + } + + public JacocoCoverageResult getClassCoverage() { + return classCoverage; + } + + public JacocoCoverageReport setClassCoverage(JacocoCoverageResult classCoverage) { + this.classCoverage = classCoverage; + return this; + } + + public JacocoCoverageResult getComplexityScore() { + return complexityScore; + } + + public JacocoCoverageReport setComplexityScore(JacocoCoverageResult complexityScore) { + this.complexityScore = complexityScore; + return this; + } + + public JacocoCoverageResult getInstructionCoverage() { + return instructionCoverage; + } + + public JacocoCoverageReport setInstructionCoverage(JacocoCoverageResult instructionCoverage) { + this.instructionCoverage = instructionCoverage; + return this; + } + + public JacocoCoverageResult getBranchCoverage() { + return branchCoverage; + } + + public JacocoCoverageReport setBranchCoverage(JacocoCoverageResult branchCoverage) { + this.branchCoverage = branchCoverage; + return this; + } + +} \ No newline at end of file diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JacocoCoverageResult.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JacocoCoverageResult.java new file mode 100644 index 00000000..41f3f43d --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JacocoCoverageResult.java @@ -0,0 +1,56 @@ +package com.offbytwo.jenkins.model; + +public class JacocoCoverageResult { + + private int covered; + private int missed; + private int percentage; + private int percentageFloat; + private int total; + + public int getCovered() { + return covered; + } + + public JacocoCoverageResult setCovered(int covered) { + this.covered = covered; + return this; + } + + public int getMissed() { + return missed; + } + + public JacocoCoverageResult setMissed(int missed) { + this.missed = missed; + return this; + } + + public int getPercentage() { + return percentage; + } + + public JacocoCoverageResult setPercentage(int percentage) { + this.percentage = percentage; + return this; + } + + public int getPercentageFloat() { + return percentageFloat; + } + + public JacocoCoverageResult setPercentageFloat(int percentageFloat) { + this.percentageFloat = percentageFloat; + return this; + } + + public int getTotal() { + return total; + } + + public JacocoCoverageResult setTotal(int total) { + this.total = total; + return this; + } + +} \ No newline at end of file diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java index b42902fc..7d5313fd 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Job.java @@ -6,31 +6,37 @@ package com.offbytwo.jenkins.model; -import static org.apache.commons.lang.StringUtils.join; - +import com.offbytwo.jenkins.client.util.EncodingUtils; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.Map; - -import com.google.common.base.Function; -import com.google.common.collect.Collections2; -import com.google.common.escape.Escaper; -import com.google.common.net.UrlEscapers; +import java.util.function.Function; +import java.util.stream.Collectors; public class Job extends BaseModel { private String name; private String url; + private String fullName; public Job() { } - + public Job(String name, String url) { this(); this.name = name; this.url = url; + this.fullName = null; + } + + public Job(String name, String url, String fullName) { + this(); + this.name = name; + this.url = url; + this.fullName = fullName; } public String getName() { @@ -40,6 +46,10 @@ public String getName() { public String getUrl() { return url; } + + public String getFullName() { + return fullName; + } public JobWithDetails details() throws IOException { return client.get(url, JobWithDetails.class); @@ -48,13 +58,13 @@ public JobWithDetails details() throws IOException { /** * Get a file from workspace. * - * @param fileName The name of the file to download from workspace. - * You can also access files which are in sub folders of the workspace. + * @param fileName The name of the file to download from workspace. You can + * also access files which are in sub folders of the workspace. * @return The string which contains the content of the file. - * @throws IOException + * @throws IOException in case of an error. */ public String getFileFromWorkspace(String fileName) throws IOException { - InputStream is = client.getFile( URI.create( url + "/ws/" + fileName )); + InputStream is = client.getFile(URI.create(url + "/ws/" + fileName)); ByteArrayOutputStream result = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int length; @@ -66,39 +76,78 @@ public String getFileFromWorkspace(String fileName) throws IOException { /** * Trigger a build without parameters + * + * @return {@link QueueReference} for further analysis of the queued build. + * @throws IOException in case of an error. */ - public void build() throws IOException { - client.post(url + "build"); + public QueueReference build() throws IOException { + ExtractHeader location = client.post(url + "build", null, ExtractHeader.class, false); + return new QueueReference(location.getLocation()); + } - public void build(boolean crumbFlag) throws IOException { - client.post(url + "build", crumbFlag); + /** + * Trigger a build with crumbFlag. + * + * @param crumbFlag true or false. + * @return {@link QueueReference} for further analysis of the queued build. + * @throws IOException in case of an error. + */ + public QueueReference build(boolean crumbFlag) throws IOException { + ExtractHeader location = client.post(url + "build", null, ExtractHeader.class, crumbFlag); + return new QueueReference(location.getLocation()); } /** - * Trigger a parameterized build + * Trigger a parameterized build with string parameters only * - * @param params - * the job parameters - * @throws IOException + * @param params the job parameters + * @return {@link QueueReference} for further analysis of the queued build. + * @throws IOException in case of an error. */ - public void build(Map params) throws IOException { - String qs = join(Collections2.transform(params.entrySet(), new MapEntryToQueryStringPair()), "&"); - client.post(url + "buildWithParameters?" + qs); + public QueueReference build(Map params) throws IOException { + return build(params, null,false); } /** - * Trigger a parameterized build + * Trigger a parameterized build with string parameters only * - * @param params - * the job parameters - * @param crumbFlag - * determines whether crumb flag is used - * @throws IOException + * @param params the job parameters + * @param crumbFlag true or false. + * @return {@link QueueReference} for further analysis of the queued build. + * @throws IOException in case of an error. */ public QueueReference build(Map params, boolean crumbFlag) throws IOException { - String qs = join(Collections2.transform(params.entrySet(), new MapEntryToQueryStringPair()), "&"); - ExtractHeader location = client.post(url + "buildWithParameters?" + qs, null, ExtractHeader.class, crumbFlag); + return build(params,null,crumbFlag); + } + + /** + * Trigger a parameterized build with file parameters + * + * @param params the job parameters + * @param fileParams the job file parameters + * @return {@link QueueReference} for further analysis of the queued build. + * @throws IOException in case of an error. + */ + public QueueReference build(Map params, Map fileParams) throws IOException { + return build(params,fileParams,false); + } + + /** + * Trigger a parameterized build with file parameters and crumbFlag + * + * @param params the job parameters + * @param fileParams the job file parameters + * @param crumbFlag determines whether crumb flag is used + * @return {@link QueueReference} for further analysis of the queued build. + * @throws IOException in case of an error. + */ + public QueueReference build(Map params, Map fileParams, boolean crumbFlag) throws IOException { + String qs = params.entrySet().stream() + .map(s -> s.getKey() + "=" + s.getValue()) + .collect(Collectors.joining("&")); +// String qs = join(Collections2.transform(params.entrySet(), new MapEntryToQueryStringPair()), "&"); + ExtractHeader location = client.post(url + "buildWithParameters?" + qs,null, ExtractHeader.class, fileParams, crumbFlag); return new QueueReference(location.getLocation()); } @@ -115,6 +164,8 @@ public boolean equals(Object o) { return false; if (url != null ? !url.equals(job.url) : job.url != null) return false; + if (fullName != null ? !fullName.equals(job.fullName) : job.fullName != null) + return false; return true; } @@ -122,15 +173,16 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = name != null ? name.hashCode() : 0; - result = 31 * result + (url != null ? url.hashCode() : 0); + result = 31 * result + (url != null ? url.hashCode() : 0) + (fullName != null ? fullName.hashCode() : 0); return result; } private static class MapEntryToQueryStringPair implements Function, String> { @Override public String apply(Map.Entry entry) { - Escaper escaper = UrlEscapers.urlFormParameterEscaper(); - return escaper.escape(entry.getKey()) + "=" + escaper.escape(entry.getValue()); + return EncodingUtils.formParameter(entry.getKey()) + "=" + EncodingUtils.formParameter(entry.getValue()); } } + + } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobConfiguration.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobConfiguration.java index 571430ab..d6a95050 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobConfiguration.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobConfiguration.java @@ -77,7 +77,8 @@ public String getConfigXml() { return configXml; } - public void setConfigXml(String configXml) { + public JobConfiguration setConfigXml(String configXml) { this.configXml = configXml; + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java index b2573293..f13903cf 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/JobWithDetails.java @@ -6,55 +6,65 @@ package com.offbytwo.jenkins.model; -import static com.google.common.collect.Lists.transform; +import com.offbytwo.jenkins.client.util.EncodingUtils; +import com.offbytwo.jenkins.helper.Range; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpResponseException; import java.io.IOException; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; -import org.apache.http.HttpStatus; -import org.apache.http.client.HttpResponseException; - -import com.google.common.base.Function; -import com.google.common.base.Optional; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; -import com.offbytwo.jenkins.client.util.EncodingUtils; -import com.offbytwo.jenkins.helper.Range; +import static com.offbytwo.jenkins.helper.FunctionalHelper.SET_CLIENT; +import static java.util.stream.Collectors.toList; public class JobWithDetails extends Job { - String displayName; + private String description; - boolean buildable; + private String displayName; - List builds; + private boolean buildable; - Build firstBuild; + private List builds = Collections.emptyList(); - Build lastBuild; + private Build firstBuild; - Build lastCompletedBuild; + private Build lastBuild; - Build lastFailedBuild; + private Build lastCompletedBuild; - Build lastStableBuild; + private Build lastFailedBuild; - Build lastSuccessfulBuild; + private Build lastStableBuild; - Build lastUnstableBuild; + private Build lastSuccessfulBuild; - Build lastUnsuccessfulBuild; + private Build lastUnstableBuild; - int nextBuildNumber; + private Build lastUnsuccessfulBuild; - boolean inQueue; + private int nextBuildNumber; - QueueItem queueItem; + private boolean inQueue; - List downstreamProjects; + private QueueItem queueItem; - List upstreamProjects; + private List downstreamProjects; + + private List upstreamProjects; + + public String getDescription() { + return description; + } + + public boolean hasDescription() { + return description != null && !description.isEmpty(); + } public String getDisplayName() { return displayName; @@ -69,26 +79,23 @@ public boolean isInQueue() { } /** - * This method will give you back the builds of a particular job.
    + * This method will give you back the builds of a particular job. + * * Note: Jenkins limits the number of results to a maximum of 100 builds * which you will get back.. In case you have more than 100 build you * won't get back all builds via this method. In such cases you need to use * {@link #getAllBuilds()}. * - * @return the list of {@link Build}. In case of no builds have been - * executed yet return {@link Collections#emptyList()}. + * @return the list of {@link Build}. In case of no builds have been + * executed yet return {@link Collections#emptyList()}. */ public List getBuilds() { if (builds == null) { return Collections.emptyList(); - } - else { - return transform(builds, new Function() { - @Override - public Build apply(Build from) { - return buildWithClient(from); - } - }); + } else { + return builds.stream() + .map(s -> buildWithClient(s)) + .collect(toList()); } } @@ -101,10 +108,9 @@ public Build apply(Build from) { * particular build {@link Build#details()} to reduce the amount of data * which needed to be transfered. * - * @return the list of {@link Build}. In case of no builds have been - * executed yet return {@link Collections#emptyList()}. - * @throws IOException - * In case of failure. + * @return the list of {@link Build}. In case of no builds have been + * executed yet return {@link Collections#emptyList()}. + * @throws IOException In case of failure. * @see Jenkins * Issue */ @@ -118,15 +124,12 @@ public List getAllBuilds() throws IOException { if (builds == null) { return Collections.emptyList(); } else { - return transform(builds, new Function() { - @Override - public Build apply(Build from) { - return buildWithClient(from); - } - }); + return builds.stream() + .map(s -> buildWithClient(s)) + .collect(toList()); } } catch (HttpResponseException e) { - // TODO: Thinks about a better handline if the job does not exist? + // TODO: Thinks about a better handling if the job does not exist? if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { // TODO: Check this if this is necessary or a good idea? @@ -153,8 +156,8 @@ public Build apply(Build from) { * {@link #getAllBuilds()}. * * @param range {@link Range} - * @return the list of {@link Build}. In case of no builds have been - * executed yet return {@link Collections#emptyList()}. + * @return the list of {@link Build}. In case of no builds have been + * executed yet return {@link Collections#emptyList()}. * @throws IOException in case of an error. */ public List getAllBuilds(Range range) throws IOException { @@ -167,12 +170,9 @@ public List getAllBuilds(Range range) throws IOException { if (builds == null) { return Collections.emptyList(); } else { - return transform(builds, new Function() { - @Override - public Build apply(Build from) { - return buildWithClient(from); - } - }); + return builds.stream() + .map(s -> buildWithClient(s)) + .collect(toList()); } } catch (HttpResponseException e) { // TODO: Thinks about a better handline if the job does not exist? @@ -196,100 +196,244 @@ private Build buildWithClient(Build from) { /** * @return the first build which has been executed or - * {@link Build#BUILD_HAS_NEVER_RAN} is this has never - * been executed. + * {@link Build#BUILD_HAS_NEVER_RUN} if the build has never been + * run. */ public Build getFirstBuild() { if (firstBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(firstBuild); } } + /** + * Check if the {@link #firstBuild} has been run or not. + * + * @return true if a build has been run false + * otherwise. + */ + public boolean hasFirstBuildRun() { + if (firstBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastBuild. If {@link #lastBuild} has never been run + * {@link Build#BUILD_HAS_NEVER_RUN} will be returned. + */ public Build getLastBuild() { if (lastBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastBuild); } } + /** + * Check if the {@link #lastBuild} has been run or not. + * + * @return true if the last build has been run + * false otherwise. + */ + public boolean hasLastBuildRun() { + if (lastBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastCompletedBuild. If {@link #lastCompletedBuild} has never + * been run {@link Build#BUILD_HAS_NEVER_RUN} will be returned. + */ public Build getLastCompletedBuild() { if (lastCompletedBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastCompletedBuild); } } + /** + * Check if the {@link #lastCompletedBuild} has been run or not. + * + * @return true if the last completed build has been run + * false otherwise. + */ + public boolean hasLastCompletedBuildRun() { + if (lastCompletedBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastFailedBuild. If {@link #lastFailedBuild} has never been + * run {@link Build#BUILD_HAS_NEVER_RUN} will be returned. + */ public Build getLastFailedBuild() { if (lastFailedBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastFailedBuild); } } + /** + * Check if the {@link #lastFailedBuild} has been run or not. + * + * @return true if the last failed build has been run + * false otherwise. + */ + public boolean hasLastFailedBuildRun() { + if (lastFailedBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastStableBuild. If {@link #lastStableBuild} has never been + * run {@link Build#BUILD_HAS_NEVER_RUN} will be returned. + */ public Build getLastStableBuild() { if (lastStableBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastStableBuild); } } + /** + * Check if the {@link #lastStableBuild} has been run or not. + * + * @return true if the last stable build has been run + * false otherwise. + */ + public boolean hasLastStableBuildRun() { + if (lastStableBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastSuccessfulBuild. If {@link #lastSuccessfulBuild} has + * never been run {@link Build#BUILD_HAS_NEVER_RUN} will be + * returned. + */ public Build getLastSuccessfulBuild() { if (lastSuccessfulBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastSuccessfulBuild); } } + /** + * Check if the {@link #lastSuccessfulBuild} has been run or not. + * + * @return true if the last successful build has been run + * false otherwise. + */ + public boolean hasLastSuccessfulBuildRun() { + if (lastSuccessfulBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastUnstableBuild. If {@link #lastUnstableBuild} has never + * been run {@link Build#BUILD_HAS_NEVER_RUN} will be returned. + */ public Build getLastUnstableBuild() { if (lastUnstableBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastUnstableBuild); } } + /** + * Check if the {@link #lastUnstableBuild} has been run or not. + * + * @return true if the last unstable build has been run + * false otherwise. + */ + public boolean hasLastUnstableBuildRun() { + if (lastUnstableBuild == null) { + return false; + } else { + return true; + } + } + + /** + * @return The lastUnsuccessfulBuild. If {@link #lastUnsuccessfulBuild} has + * never been run {@link Build#BUILD_HAS_NEVER_RUN} will be + * returned. + */ public Build getLastUnsuccessfulBuild() { if (lastUnsuccessfulBuild == null) { - return Build.BUILD_HAS_NEVER_RAN; + return Build.BUILD_HAS_NEVER_RUN; } else { return buildWithClient(lastUnsuccessfulBuild); } } + /** + * Check if the {@link #lastUnsuccessfulBuild} has been run or not. + * + * @return true if the last unsuccessful build has been run + * false otherwise. + */ + public boolean hasLastUnsuccessfulBuildRun() { + if (lastUnsuccessfulBuild == null) { + return false; + } else { + return true; + } + } + public int getNextBuildNumber() { return nextBuildNumber; } /** - * @return the list of downstream projects. - * If no downstream projects exist just return - * an {@link Collections#emptyList()} + * @return the list of downstream projects. If no downstream projects exist + * just return an empty list {@link Collections#emptyList()}. */ public List getDownstreamProjects() { if (downstreamProjects == null) { return Collections.emptyList(); } else { - return transform(downstreamProjects, new JobWithClient()); + return downstreamProjects.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); } } /** - * @return the list of upstream projects. - * If no upstream projects exist just return - * an {@link Collections#emptyList()} + * @return the list of upstream projects. If no upstream projects exist just + * return an empty list {@link Collections#emptyList()}. */ public List getUpstreamProjects() { if (upstreamProjects == null) { return Collections.emptyList(); } else { - return transform(upstreamProjects, new JobWithClient()); + return upstreamProjects.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); } } @@ -297,27 +441,83 @@ public QueueItem getQueueItem() { return this.queueItem; } - public Build getBuildByNumber(final int buildNumber) { + /** + * Get a build by the given buildNumber. + * + * @param buildNumber The number to select the build by. + * @return The an Optional with the {@link Build} selected by the given buildnumber + * + */ + public Optional getBuildByNumber(final int buildNumber) { + return builds.stream().filter(isBuildNumberEqualTo(buildNumber)).findFirst(); + } + + /** + * Get a module of a {@link Job} + * + * @param moduleName name of the {@link MavenModule} + * @return The {@link MavenModuleWithDetails} selected by the given module name + * @throws java.io.IOException in case of errors. + * + */ + public MavenModuleWithDetails getModule(String moduleName) throws IOException { + return client.get(getUrl() + moduleName, MavenModuleWithDetails.class); + } - Predicate isMatchingBuildNumber = new Predicate() { + /** + * Empty description to be used for {@link #updateDescription(String)} or + * {@link #updateDescription(String, boolean)}. + */ + public static final String EMPTY_DESCRIPTION = ""; - @Override - public boolean apply(Build input) { - return input.getNumber() == buildNumber; - } - }; + /** + * Update the description of a Job. + * + * @param description The description which should be set. If you like to + * set an empty description you should use + * {@link #EMPTY_DESCRIPTION}. + * @throws IOException in case of errors. + */ + public JobWithDetails updateDescription(String description) throws IOException { + return updateDescription(description, false); + } - Optional optionalBuild = Iterables.tryFind(builds, isMatchingBuildNumber); - //TODO: Check if we could use Build#NO...instead of Null? - return optionalBuild.orNull() == null ? null : buildWithClient(optionalBuild.orNull()); + /** + * Update the description of a Job. + * + * @param description The description which should be set. If you like to + * set an empty description you should use + * {@link #EMPTY_DESCRIPTION}. + * @param crumbFlag true or false. + * @throws IOException in case of errors. + */ + public JobWithDetails updateDescription(String description, boolean crumbFlag) throws IOException { + Objects.requireNonNull(description, "description is not allowed to be null."); + //JDK9+ + // Map.of(...); + Map params = new HashMap<>(); + params.put("description", description); + client.post_form(this.getUrl() + "/submitDescription?", params, crumbFlag); + return this; } - private class JobWithClient implements Function { - @Override - public Job apply(Job job) { - job.setClient(client); - return job; - } + /** + * clear the description of a job. + * + * @throws IOException in case of errors. + */ + public JobWithDetails clearDescription() throws IOException { + return updateDescription(EMPTY_DESCRIPTION); + } + + /** + * clear the description of a job. + * + * @param crumbFlag true or false. + * @throws IOException in case of errors. + */ + public JobWithDetails clearDescription(boolean crumbFlag) throws IOException { + return updateDescription(EMPTY_DESCRIPTION, crumbFlag); } @Override @@ -326,6 +526,7 @@ public int hashCode() { int result = super.hashCode(); result = prime * result + (buildable ? 1231 : 1237); result = prime * result + ((builds == null) ? 0 : builds.hashCode()); + result = prime * result + ((description == null) ? 0 : description.hashCode()); result = prime * result + ((displayName == null) ? 0 : displayName.hashCode()); result = prime * result + ((downstreamProjects == null) ? 0 : downstreamProjects.hashCode()); result = prime * result + ((firstBuild == null) ? 0 : firstBuild.hashCode()); @@ -359,6 +560,11 @@ public boolean equals(Object obj) { return false; } else if (!builds.equals(other.builds)) return false; + if (description == null) { + if (other.description != null) + return false; + } else if (!description.equals(other.description)) + return false; if (displayName == null) { if (other.displayName != null) return false; diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/LoadStatistics.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/LoadStatistics.java index df2a5608..fc335e5e 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/LoadStatistics.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/LoadStatistics.java @@ -18,24 +18,27 @@ public HourMinSec10 getBusyExecutors() { return busyExecutors; } - public void setBusyExecutors(HourMinSec10 busyExecutors) { + public LoadStatistics setBusyExecutors(HourMinSec10 busyExecutors) { this.busyExecutors = busyExecutors; + return this; } public HourMinSec10 getQueueLength() { return queueLength; } - public void setQueueLength(HourMinSec10 queueLength) { + public LoadStatistics setQueueLength(HourMinSec10 queueLength) { this.queueLength = queueLength; + return this; } public HourMinSec10 getTotalExecutors() { return totalExecutors; } - public void setTotalExecutors(HourMinSec10 totalExecutors) { + public LoadStatistics setTotalExecutors(HourMinSec10 totalExecutors) { this.totalExecutors = totalExecutors; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java index b239558d..167f6f40 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MainView.java @@ -6,11 +6,10 @@ package com.offbytwo.jenkins.model; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import com.google.common.collect.Lists; - public class MainView extends BaseModel { private List jobs; @@ -18,7 +17,7 @@ public class MainView extends BaseModel { /* default constructor needed for Jackson */ public MainView() { - this(Lists.newArrayList(), Lists.newArrayList()); + this(new ArrayList<>(), new ArrayList<>()); } public MainView(List jobs, List views) { @@ -27,22 +26,24 @@ public MainView(List jobs, List views) { } public MainView(Job... jobs) { - this(Arrays.asList(jobs), Lists.newArrayList()); + this(Arrays.asList(jobs), new ArrayList<>()); } public List getJobs() { return jobs; } - public void setJobs(List jobs) { + public MainView setJobs(List jobs) { this.jobs = jobs; + return this; } public List getViews() { return views; } - public void setViews(List views) { + public MainView setViews(List views) { this.views = views; + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenArtifact.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenArtifact.java index fe78091e..c51ea4ea 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenArtifact.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenArtifact.java @@ -2,14 +2,14 @@ public class MavenArtifact extends BaseModel { - String artifactId; - String canonicalName; - String classifier; - String fileName; - String groupId; - String md5sum; - String type; - String version; + private String artifactId; + private String canonicalName; + private String classifier; + private String fileName; + private String groupId; + private String md5sum; + private String type; + private String version; public MavenArtifact() { } @@ -18,63 +18,71 @@ public String getArtifactId() { return artifactId; } - public void setArtifactId(String artifactId) { + public MavenArtifact setArtifactId(String artifactId) { this.artifactId = artifactId; + return this; } public String getCanonicalName() { return canonicalName; } - public void setCanonicalName(String canonicalName) { + public MavenArtifact setCanonicalName(String canonicalName) { this.canonicalName = canonicalName; + return this; } public String getClassifier() { return classifier; } - public void setClassifier(String classifier) { + public MavenArtifact setClassifier(String classifier) { this.classifier = classifier; + return this; } public String getFileName() { return fileName; } - public void setFileName(String fileName) { + public MavenArtifact setFileName(String fileName) { this.fileName = fileName; + return this; } public String getGroupId() { return groupId; } - public void setGroupId(String groupId) { + public MavenArtifact setGroupId(String groupId) { this.groupId = groupId; + return this; } public String getMd5sum() { return md5sum; } - public void setMd5sum(String md5sum) { + public MavenArtifact setMd5sum(String md5sum) { this.md5sum = md5sum; + return this; } public String getType() { return type; } - public void setType(String type) { + public MavenArtifact setType(String type) { this.type = type; + return this; } public String getVersion() { return version; } - public void setVersion(String version) { + public MavenArtifact setVersion(String version) { this.version = version; + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenBuild.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenBuild.java index ec46719a..2ab8de4b 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenBuild.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenBuild.java @@ -4,6 +4,18 @@ public class MavenBuild extends Build { + /** + * This will be returned by the API in cases where no build has ever + * been executed like {@link JobWithDetails#getLastBuild()} etc. + */ + public static final MavenBuild BUILD_HAS_NEVER_RUN = new MavenBuild(-1, -1, "UNKNOWN"); + + private MavenBuild(int number, int queueId, String url) { + setNumber(number); + setQueueId(queueId); + setUrl(url); + } + public MavenBuild() { } @@ -19,7 +31,7 @@ public MavenModule getMavenModule() throws IOException { return client.get(this.getUrl() + "/mavenArtifacts/", MavenModule.class); } - public TestReport getTestReport() throws IOException { - return client.get(this.getUrl() + "/testReport/?depth=1", TestReport.class); + public JacocoCoverageReport getJacocoCodeCoverageReport() throws IOException { + return client.get(this.getUrl() + "/jacoco/", JacocoCoverageReport.class); } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJob.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJob.java index 1815d22f..1204c844 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJob.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJob.java @@ -10,6 +10,10 @@ public MavenJob() { public MavenJob(String name, String url) { super(name, url); } + + public MavenJob(String name, String url, String fullName) { + super(name, url, fullName); + } public MavenJobWithDetails mavenDetails() throws IOException { return client.get(getUrl(), MavenJobWithDetails.class); diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJobWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJobWithDetails.java index ae30ee55..3eaf7fc0 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJobWithDetails.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenJobWithDetails.java @@ -1,25 +1,34 @@ package com.offbytwo.jenkins.model; +import com.offbytwo.jenkins.client.util.EncodingUtils; +import com.offbytwo.jenkins.helper.Range; +import org.apache.http.HttpStatus; +import org.apache.http.client.HttpResponseException; + +import java.io.IOException; +import java.util.Collections; import java.util.List; +import java.util.Optional; -import com.google.common.base.Function; -import com.google.common.collect.Lists; +import static com.offbytwo.jenkins.helper.FunctionalHelper.SET_CLIENT; +import static java.util.stream.Collectors.toList; public class MavenJobWithDetails extends MavenJob { - String displayName; - boolean buildable; - List builds; - MavenBuild lastBuild; - MavenBuild lastCompletedBuild; - MavenBuild lastFailedBuild; - MavenBuild lastStableBuild; - MavenBuild lastSuccessfulBuild; - MavenBuild lastUnstableBuild; - MavenBuild lastUnsuccessfulBuild; - int nextBuildNumber; - List downstreamProjects; - List upstreamProjects; + private String displayName; + private boolean buildable; + private List builds; + private MavenBuild firstBuild; + private MavenBuild lastBuild; + private MavenBuild lastCompletedBuild; + private MavenBuild lastFailedBuild; + private MavenBuild lastStableBuild; + private MavenBuild lastSuccessfulBuild; + private MavenBuild lastUnstableBuild; + private MavenBuild lastUnsuccessfulBuild; + private int nextBuildNumber; + private List downstreamProjects; + private List upstreamProjects; public MavenJobWithDetails() { } @@ -32,41 +41,213 @@ public boolean isBuildable() { return buildable; } + /** + * This method will give you back the builds of a particular job. + * + * Note: Jenkins limits the number of results to a maximum of 100 builds + * which you will get back.. In case you have more than 100 build you + * won't get back all builds via this method. In such cases you need to use + * {@link #getAllBuilds()}. + * + * @return the list of {@link MavenBuild}. In case of no builds have been + * executed yet {@link Collections#emptyList()} will be returned. + */ public List getBuilds() { - return Lists.transform(builds, new Function() { - @Override - public MavenBuild apply(MavenBuild from) { - return buildWithClient(from); + if (builds == null) { + return Collections.emptyList(); + } else { + return builds.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); + } + } + + /** + * This method will give you back all builds which exists independent of the + * number. You should be aware that this can be much in some cases if you + * have more than 100 builds which is by default limited by Jenkins + * {@link #getBuilds()}. This method limits it to particular information + * which can be later used to get supplemental information about a + * particular build {@link Build#details()} to reduce the amount of data + * which needed to be transfered. + * + * @return the list of {@link Build}. In case of no builds have been + * executed yet return {@link Collections#emptyList()}. + * @throws IOException + * In case of failure. + * @see Jenkins + * Issue + */ + public List getAllBuilds() throws IOException { + String path = "/"; + + try { + List builds = client.get(path + "job/" + EncodingUtils.encode(this.getName()) + + "?tree=allBuilds[number[*],url[*],queueId[*]]", AllMavenBuilds.class).getAllBuilds(); + + if (builds == null) { + return Collections.emptyList(); + } else { + return builds.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); + } + } catch (HttpResponseException e) { + // TODO: Thinks about a better handling if the job does not exist? + if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + // TODO: Check this if this is necessary or a good idea? + + return null; } - }); + throw e; + } + } + + /** + * + *
      + *
    • {M,N}: From the M-th element (inclusive) to the N-th element + * (exclusive).
    • + *
    • {M,}: From the M-th element (inclusive) to the end.
    • + *
    • {,N}: From the first element (inclusive) to the N-th element + * (exclusive). The same as {0,N}.
    • + *
    • {N}: Just retrieve the N-th element. The same as {N,N+1}.
    • + *
    + * + * Note: At the moment there seemed to be no option to get the number of + * existing builds for a job. The only option is to get all builds via + * {@link #getAllBuilds()}. + * + * @param range + * {@link Range} + * @return the list of {@link Build}. In case of no builds have been + * executed yet return {@link Collections#emptyList()}. + * @throws IOException + * in case of an error. + */ + public List getAllBuilds(Range range) throws IOException { + String path = "/" + "job/" + EncodingUtils.encode(this.getName()) + + "?tree=allBuilds[number[*],url[*],queueId[*]]"; + + try { + List builds = client.get(path + range.getRangeString(), AllMavenBuilds.class).getAllBuilds(); + if (builds == null) { + return Collections.emptyList(); + } else { + return builds.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); + } + } catch (HttpResponseException e) { + // TODO: Thinks about a better handline if the job does not exist? + if (e.getStatusCode() == HttpStatus.SC_NOT_FOUND) { + // TODO: Check this if this is necessary or a good idea? + + return null; + } + throw e; + } + } + + + /** + * @return The firstBuild. If {@link #firstBuild} has never been run + * {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be returned. + */ + public MavenBuild getFirstBuild() { + if (firstBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(firstBuild); + } + } + + /** + * @return The lastBuild. If {@link #lastBuild} has never been run + * {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be returned. + */ public MavenBuild getLastBuild() { - return buildWithClient(lastBuild); + if (lastBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastBuild); + } } + /** + * @return The lastCompletedBuild. If {@link #lastCompletedBuild} has never + * been run {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be returned. + */ public MavenBuild getLastCompletedBuild() { - return buildWithClient(lastCompletedBuild); + if (lastCompletedBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastCompletedBuild); + } } + /** + * @return The lastFailedBuild. If {@link #lastFailedBuild} has never been + * run {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be returned. + */ public MavenBuild getLastFailedBuild() { - return buildWithClient(lastFailedBuild); + if (lastFailedBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastFailedBuild); + } } + /** + * @return The lastStableBuild. If {@link #lastStableBuild} has never been + * run {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be returned. + */ public MavenBuild getLastStableBuild() { - return buildWithClient(lastStableBuild); + if (lastStableBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastStableBuild); + } } + /** + * @return The lastSuccessfulBuild. If {@link #lastSuccessfulBuild} has + * never been run {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be + * returned. + */ public MavenBuild getLastSuccessfulBuild() { - return buildWithClient(lastSuccessfulBuild); + if (lastSuccessfulBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastSuccessfulBuild); + } } + /** + * @return The lastUnstableBuild. If {@link #lastUnstableBuild} has never + * been run {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be returned. + */ public MavenBuild getLastUnstableBuild() { - return buildWithClient(lastUnstableBuild); + if (lastUnstableBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastUnstableBuild); + } } + /** + * @return The lastUnsuccessfulBuild. If {@link #lastUnsuccessfulBuild} has + * never been run {@link MavenBuild#BUILD_HAS_NEVER_RUN} will be + * returned. + */ public MavenBuild getLastUnsuccessfulBuild() { - return buildWithClient(lastUnsuccessfulBuild); + if (lastUnsuccessfulBuild == null) { + return MavenBuild.BUILD_HAS_NEVER_RUN; + } else { + return buildWithClient(lastUnsuccessfulBuild); + } } public int getNextBuildNumber() { @@ -74,24 +255,39 @@ public int getNextBuildNumber() { } public List getDownstreamProjects() { - return Lists.transform(downstreamProjects, new MavenJobWithClient()); + if (downstreamProjects == null) { + return Collections.emptyList(); + } else { + return downstreamProjects.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); + } } public List getUpstreamProjects() { - return Lists.transform(upstreamProjects, new MavenJobWithClient()); + if (upstreamProjects == null) { + return Collections.emptyList(); + } else { + return upstreamProjects.stream() + .map(SET_CLIENT(this.client)) + .collect(toList()); + } } + /** + * @param buildNumber The build you would like to select. + * @return Optional which contains the {@link MavenBuild}. + */ + public Optional getBuildByNumber(final int buildNumber) { + return builds.stream() + .filter(isBuildNumberEqualTo(buildNumber)) + .findFirst(); + } + private MavenBuild buildWithClient(MavenBuild from) { MavenBuild ret = new MavenBuild(from); ret.setClient(client); return ret; } - private class MavenJobWithClient implements Function { - @Override - public Job apply(Job job) { - job.setClient(client); - return job; - } - } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModule.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModule.java index b3f4eeb8..91c7ef1c 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModule.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModule.java @@ -1,10 +1,59 @@ +/* + * Copyright (c) 2018 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ package com.offbytwo.jenkins.model; +import java.io.IOException; import java.util.List; - +/** + * + * @author Karl Heinz Marbaise, Ricardo Zanini, René Scheibe, Jakub Zacek + */ public class MavenModule extends BaseModel { - List moduleRecords; + private List moduleRecords; + private String name; + private String url; + private String color; + private String displayName; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public MavenModuleWithDetails details() throws IOException { + return client.get(url, MavenModuleWithDetails.class); + } public List getModuleRecords() { return moduleRecords; diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleRecord.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleRecord.java index d8fa13cd..34fbd424 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleRecord.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleRecord.java @@ -4,11 +4,11 @@ public class MavenModuleRecord extends BaseModel { - List attachedArtifacts; - Build parent; - MavenArtifact mainArtifact; - MavenArtifact pomArtifact; - String url; + private List attachedArtifacts; + private Build parent; + private MavenArtifact mainArtifact; + private MavenArtifact pomArtifact; + private String url; public MavenModuleRecord() { } @@ -17,39 +17,44 @@ public List getAttachedArtifacts() { return attachedArtifacts; } - public void setAttachedArtifacts(List attachedArtifacts) { + public MavenModuleRecord setAttachedArtifacts(List attachedArtifacts) { this.attachedArtifacts = attachedArtifacts; + return this; } public Build getParent() { return parent; } - public void setParent(Build parent) { + public MavenModuleRecord setParent(Build parent) { this.parent = parent; + return this; } public MavenArtifact getMainArtifact() { return mainArtifact; } - public void setMainArtifact(MavenArtifact mainArtifact) { + public MavenModuleRecord setMainArtifact(MavenArtifact mainArtifact) { this.mainArtifact = mainArtifact; + return this; } public MavenArtifact getPomArtifact() { return pomArtifact; } - public void setPomArtifact(MavenArtifact pomArtifact) { + public MavenModuleRecord setPomArtifact(MavenArtifact pomArtifact) { this.pomArtifact = pomArtifact; + return this; } public String getUrl() { return url; } - public void setUrl(String url) { + public MavenModuleRecord setUrl(String url) { this.url = url; + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleWithDetails.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleWithDetails.java new file mode 100644 index 00000000..d168a45e --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/MavenModuleWithDetails.java @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.model; + +import static com.offbytwo.jenkins.helper.FunctionalHelper.SET_CLIENT; +import static java.util.stream.Collectors.toList; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Model Class for Maven Modules + * + * @author Jakub Zacek + */ +public class MavenModuleWithDetails extends BaseModel { + + private List builds = Collections.emptyList(); + private List actions = Collections.emptyList(); + private String displayName; + private BuildResult result; + private String url; + private long duration; + private long timestamp; + + public List getActions() { + return actions; + } + + public void setActions(List actions) { + this.actions = actions; + } + + public void setBuilds(List builds) { + this.builds = builds; + } + + public List getBuilds() { + return builds.stream() + .map(SET_CLIENT(this.getClient())) + .collect(toList()); + } + + public Build getBuildByNumber(final int buildNumber) { + return builds.stream() + .filter(isBuildNumberEqualTo(buildNumber)) + .map(SET_CLIENT(this.getClient())) + .findFirst() + .orElse(Build.BUILD_HAS_NEVER_RUN); + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public BuildResult getResult() { + return result; + } + + public void setResult(BuildResult result) { + this.result = result; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + public String getConsoleOutputText() throws IOException { + return client.get(getUrl() + "/logText/progressiveText"); + } + + public TestReport getTestReport() throws IOException { + return client.get(this.getUrl() + "/testReport/?depth=1", TestReport.class); + } + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/OfflineCause.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/OfflineCause.java index 66c70cd1..30969dc9 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/OfflineCause.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/OfflineCause.java @@ -17,6 +17,16 @@ public String getDescription() { return description; } + public OfflineCause setTimestamp(Long timestamp) { + this.timestamp = timestamp; + return this; + } + + public OfflineCause setDescription(String description) { + this.description = description; + return this; + } + @Override public int hashCode() { final int prime = 31; @@ -48,12 +58,4 @@ public boolean equals(Object obj) { return true; } - public void setTimestamp(Long timestamp) { - this.timestamp = timestamp; - } - - public void setDescription(String description) { - this.description = description; - } - } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParameterDefinitions.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParameterDefinitions.java index b46c69e4..47247c74 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParameterDefinitions.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParameterDefinitions.java @@ -30,11 +30,13 @@ public List getStringParams() { return stringParams; } - public void setStringParams(List stringParams) { + public ParameterDefinitions setStringParams(List stringParams) { this.stringParams = stringParams; + return this; } - public void addParam(StringParameterDefinition spd) { + public ParameterDefinitions addParam(StringParameterDefinition spd) { stringParams.add(spd); + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParametersDefinitionProperty.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParametersDefinitionProperty.java index efb033f5..bb88a128 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParametersDefinitionProperty.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/ParametersDefinitionProperty.java @@ -26,7 +26,8 @@ public ParameterDefinitions getPd() { return pd; } - public void setPd(ParameterDefinitions pd) { + public ParametersDefinitionProperty setPd(ParameterDefinitions pd) { this.pd = pd; + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Plugin.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Plugin.java index fe380202..6c9e09a1 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Plugin.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Plugin.java @@ -27,9 +27,10 @@ public boolean isActive() return active; } - public void setActive( boolean active ) + public Plugin setActive(boolean active) { this.active = active; + return this; } public String getBackupVersion() @@ -37,9 +38,10 @@ public String getBackupVersion() return backupVersion; } - public void setBackupVersion( String backupVersion ) + public Plugin setBackupVersion(String backupVersion) { this.backupVersion = backupVersion; + return this; } public boolean isBundled() @@ -47,9 +49,10 @@ public boolean isBundled() return bundled; } - public void setBundled( boolean bundled ) + public Plugin setBundled(boolean bundled) { this.bundled = bundled; + return this; } public boolean isDowngradable() @@ -57,9 +60,10 @@ public boolean isDowngradable() return downgradable; } - public void setDowngradable( boolean downgradable ) + public Plugin setDowngradable(boolean downgradable) { this.downgradable = downgradable; + return this; } public boolean isEnabled() @@ -67,9 +71,10 @@ public boolean isEnabled() return enabled; } - public void setEnabled( boolean enabled ) + public Plugin setEnabled(boolean enabled) { this.enabled = enabled; + return this; } public boolean isHasUpdate() @@ -77,9 +82,10 @@ public boolean isHasUpdate() return hasUpdate; } - public void setHasUpdate( boolean hasUpdate ) + public Plugin setHasUpdate(boolean hasUpdate) { this.hasUpdate = hasUpdate; + return this; } public String getLongName() @@ -87,9 +93,10 @@ public String getLongName() return longName; } - public void setLongName( String longName ) + public Plugin setLongName(String longName) { this.longName = longName; + return this; } public boolean isPinned() @@ -97,9 +104,10 @@ public boolean isPinned() return pinned; } - public void setPinned( boolean pinned ) + public Plugin setPinned(boolean pinned) { this.pinned = pinned; + return this; } public String getShortName() @@ -107,9 +115,10 @@ public String getShortName() return shortName; } - public void setShortName( String shortName ) + public Plugin setShortName(String shortName) { this.shortName = shortName; + return this; } public String getSupportsDynamicLoad() @@ -117,9 +126,10 @@ public String getSupportsDynamicLoad() return supportsDynamicLoad; } - public void setSupportsDynamicLoad( String supportsDynamicLoad ) + public Plugin setSupportsDynamicLoad(String supportsDynamicLoad) { this.supportsDynamicLoad = supportsDynamicLoad; + return this; } public String getUrl() @@ -127,9 +137,10 @@ public String getUrl() return url; } - public void setUrl( String url ) + public Plugin setUrl(String url) { this.url = url; + return this; } public String getVersion() @@ -137,9 +148,10 @@ public String getVersion() return version; } - public void setVersion( String version ) + public Plugin setVersion(String version) { this.version = version; + return this; } public List getDependencies() @@ -147,9 +159,10 @@ public List getDependencies() return dependencies; } - public void setDependencies( List dependencies ) + public Plugin setDependencies(List dependencies) { this.dependencies = dependencies; + return this; } @Override @@ -247,6 +260,4 @@ else if ( !version.equals( other.version ) ) return true; } - - } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginDependency.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginDependency.java index adb1331d..acac798d 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginDependency.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginDependency.java @@ -11,6 +11,33 @@ public class PluginDependency extends BaseModel private String version; + public boolean isOptional() { + return optional; + } + + public PluginDependency setOptional(boolean optional) { + this.optional = optional; + return this; + } + + public String getShortName() { + return shortName; + } + + public PluginDependency setShortName(String shortName) { + this.shortName = shortName; + return this; + } + + public String getVersion() { + return version; + } + + public PluginDependency setVersion(String version) { + this.version = version; + return this; + } + @Override public int hashCode() { @@ -51,34 +78,4 @@ else if ( !version.equals( other.version ) ) return true; } - public boolean isOptional() - { - return optional; - } - - public void setOptional( boolean optional ) - { - this.optional = optional; - } - - public String getShortName() - { - return shortName; - } - - public void setShortName( String shortName ) - { - this.shortName = shortName; - } - - public String getVersion() - { - return version; - } - - public void setVersion( String version ) - { - this.version = version; - } - } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginManager.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginManager.java index ea1bd5e7..c08fc713 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginManager.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/PluginManager.java @@ -7,12 +7,17 @@ */ public class PluginManager extends BaseModel { - List plugins; + private List plugins; public List getPlugins() { return plugins; } + + public PluginManager setPlugins(List plugins) { + this.plugins = plugins; + return this; + } @Override public int hashCode() diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Queue.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Queue.java index 2e484dd5..355dc910 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Queue.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Queue.java @@ -1,9 +1,14 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + package com.offbytwo.jenkins.model; import java.util.List; -public class Queue - extends BaseModel +public class Queue extends BaseModel { private List discoverableItems; @@ -18,9 +23,10 @@ public List getDiscoverableItems() return discoverableItems; } - public void setDiscoverableItems( List discoverableItems ) + public Queue setDiscoverableItems(List discoverableItems) { this.discoverableItems = discoverableItems; + return this; } @Override @@ -38,9 +44,10 @@ public List getItems() return items; } - public void setItems( List items ) + public Queue setItems(List items) { this.items = items; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItem.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItem.java index 1ada54b9..ae2d8d3d 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItem.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItem.java @@ -1,7 +1,16 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + package com.offbytwo.jenkins.model; +import java.util.List; + public class QueueItem extends BaseModel { - // actions + + private List actions; private boolean blocked; @@ -15,103 +24,129 @@ public class QueueItem extends BaseModel { private boolean stuck; - // task - // name - // url - // color? + private QueueTask task; private String url; - + private String why; private boolean cancelled; private Executable executable; + public List getActions() { + return actions; + } + + public QueueItem setActions(List actions) { + this.actions = actions; + return this; + } + public boolean isBlocked() { return blocked; } - public void setBlocked(boolean blocked) { + public QueueItem setBlocked(boolean blocked) { this.blocked = blocked; + return this; } public boolean isBuildable() { return buildable; } - public void setBuildable(boolean buildable) { + public QueueItem setBuildable(boolean buildable) { this.buildable = buildable; + return this; } public Long getId() { return id; } - public void setId(Long id) { + public QueueItem setId(Long id) { this.id = id; + return this; } public Long getInQueueSince() { return inQueueSince; } - public void setInQueueSince(Long inQueueSince) { + public QueueItem setInQueueSince(Long inQueueSince) { this.inQueueSince = inQueueSince; + return this; } public String getParams() { return params; } - public void setParams(String params) { + public QueueItem setParams(String params) { this.params = params; + return this; } public boolean isStuck() { return stuck; } - public void setStuck(boolean stuck) { + public QueueTask getTask() { + return task; + } + + public QueueItem setTask(QueueTask task) { + this.task = task; + return this; + } + + public QueueItem setStuck(boolean stuck) { this.stuck = stuck; + return this; } public String getUrl() { return url; } - public void setUrl(String url) { + public QueueItem setUrl(String url) { this.url = url; + return this; } public String getWhy() { return why; } - public void setWhy(String why) { + public QueueItem setWhy(String why) { this.why = why; + return this; } public boolean isCancelled() { return cancelled; } - public void setCancelled(boolean cancelled) { + public QueueItem setCancelled(boolean cancelled) { this.cancelled = cancelled; + return this; } public Executable getExecutable() { return executable; } - public void setExecutable(Executable executable) { + public QueueItem setExecutable(Executable executable) { this.executable = executable; + return this; } @Override public int hashCode() { final int prime = 31; int result = 1; + result = prime * result + ((actions == null) ? 0 : actions.hashCode()); result = prime * result + (blocked ? 1231 : 1237); result = prime * result + (buildable ? 1231 : 1237); result = prime * result + (cancelled ? 1231 : 1237); @@ -120,6 +155,7 @@ public int hashCode() { result = prime * result + ((inQueueSince == null) ? 0 : inQueueSince.hashCode()); result = prime * result + ((params == null) ? 0 : params.hashCode()); result = prime * result + (stuck ? 1231 : 1237); + result = prime * result + ((task == null) ? 0 : task.hashCode()); result = prime * result + ((url == null) ? 0 : url.hashCode()); result = prime * result + ((why == null) ? 0 : why.hashCode()); return result; @@ -134,6 +170,11 @@ public boolean equals(Object obj) { if (getClass() != obj.getClass()) return false; QueueItem other = (QueueItem) obj; + if (actions == null) { + if (other.actions != null) + return false; + } else if (!actions.equals(other.actions)) + return false; if (blocked != other.blocked) return false; if (buildable != other.buildable) @@ -162,6 +203,11 @@ public boolean equals(Object obj) { return false; if (stuck != other.stuck) return false; + if (task == null) { + if (other.task != null) + return false; + } else if (!task.equals(other.task)) + return false; if (url == null) { if (other.url != null) return false; diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItemActions.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItemActions.java new file mode 100644 index 00000000..cbd6e390 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueItemActions.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + +package com.offbytwo.jenkins.model; + +import java.util.List; + +public class QueueItemActions extends BaseModel { + private List causes; + + public List getCauses() { + return causes; + } + + public QueueItemActions setCauses(List causes) { + this.causes = causes; + return this; + } + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueTask.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueTask.java new file mode 100644 index 00000000..4cc53978 --- /dev/null +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/QueueTask.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ + +package com.offbytwo.jenkins.model; + +public class QueueTask extends BaseModel { + + private String name; + + private String url; + + private String color; + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + + public String getColor() { + return color; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((color == null) ? 0 : color.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((url == null) ? 0 : url.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QueueTask other = (QueueTask) obj; + if (color == null) { + if (other.color != null) + return false; + } else if (!color.equals(other.color)) + return false; + if (name == null) { + if (other.name != null) + return false; + } else if (!name.equals(other.name)) + return false; + if (url == null) { + if (other.url != null) + return false; + } else if (!url.equals(other.url)) + return false; + return true; + } + +} diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Statis.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Statis.java index b2c601c3..bb920ea4 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Statis.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/Statis.java @@ -22,16 +22,18 @@ public List getHistory() { return history; } - public void setHistory(List history) { + public Statis setHistory(List history) { this.history = history; + return this; } public Double getLatest() { return latest; } - public void setLatest(Double latest) { + public Statis setLatest(Double latest) { this.latest = latest; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/StringParameterDefinition.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/StringParameterDefinition.java index 740e3d77..2a89618f 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/StringParameterDefinition.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/StringParameterDefinition.java @@ -34,23 +34,26 @@ public String getName() { return name; } - public void setName(String name) { + public StringParameterDefinition setName(String name) { this.name = name; + return this; } public String getDescription() { return description; } - public void setDescription(String description) { + public StringParameterDefinition setDescription(String description) { this.description = description; + return this; } public String getDefaultValue() { return defaultValue; } - public void setDefaultValue(String defaultValue) { + public StringParameterDefinition setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; + return this; } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestCase.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestCase.java index 8f4f3795..f3ac1331 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestCase.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestCase.java @@ -21,9 +21,10 @@ public int getAge() return age; } - public void setAge( int age ) + public TestCase setAge(int age) { this.age = age; + return this; } public String getClassName() @@ -31,9 +32,10 @@ public String getClassName() return className; } - public void setClassName( String className ) + public TestCase setClassName(String className) { this.className = className; + return this; } public double getDuration() @@ -41,9 +43,10 @@ public double getDuration() return duration; } - public void setDuration( double duration ) + public TestCase setDuration(double duration) { this.duration = duration; + return this; } public int getFailedSince() @@ -51,9 +54,10 @@ public int getFailedSince() return failedSince; } - public void setFailedSince( int failedSince ) + public TestCase setFailedSince(int failedSince) { this.failedSince = failedSince; + return this; } public String getName() @@ -61,9 +65,10 @@ public String getName() return name; } - public void setName( String name ) + public TestCase setName(String name) { this.name = name; + return this; } public boolean isSkipped() @@ -71,9 +76,10 @@ public boolean isSkipped() return skipped; } - public void setSkipped( boolean skipped ) + public TestCase setSkipped(boolean skipped) { this.skipped = skipped; + return this; } public String getStatus() @@ -81,9 +87,10 @@ public String getStatus() return status; } - public void setStatus( String status ) + public TestCase setStatus(String status) { this.status = status; + return this; } public String getErrorDetails() @@ -91,9 +98,10 @@ public String getErrorDetails() return errorDetails; } - public void setErrorDetails( String errorDetails ) + public TestCase setErrorDetails(String errorDetails) { this.errorDetails = errorDetails; + return this; } public String getErrorStackTrace() @@ -101,9 +109,10 @@ public String getErrorStackTrace() return errorStackTrace; } - public void setErrorStackTrace( String errorStackTrace ) + public TestCase setErrorStackTrace(String errorStackTrace) { this.errorStackTrace = errorStackTrace; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChild.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChild.java index 516bb446..a8d50ca0 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChild.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChild.java @@ -12,16 +12,18 @@ public int getNumber() { return number; } - public void setNumber(int number) { + public TestChild setNumber(int number) { this.number = number; + return this; } public String getUrl() { return url; } - public void setUrl(String url) { + public TestChild setUrl(String url) { this.url = url; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChildReport.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChildReport.java index da920bec..7d3ea3f5 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChildReport.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestChildReport.java @@ -13,8 +13,18 @@ public TestChild getChild() { return child; } - public void setChild(TestChild child) { + public TestChildReport setChild(TestChild child) { this.child = child; + return this; + } + + public TestResult getResult() { + return result; + } + + public TestChildReport setResult(TestResult result) { + this.result = result; + return this; } @Override @@ -48,12 +58,4 @@ public boolean equals(Object obj) { return true; } - public TestResult getResult() { - return result; - } - - public void setResult(TestResult result) { - this.result = result; - } - } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestReport.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestReport.java index 808f7d30..de3f0162 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestReport.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestReport.java @@ -1,5 +1,6 @@ package com.offbytwo.jenkins.model; +import java.util.Collections; import java.util.List; /** @@ -8,51 +9,83 @@ */ public class TestReport extends BaseModel { + public static final String EMPTY_STRING = ""; + + /** + * This will be returned by the API in cases where the build has not been + * run. + */ + public static final TestReport NO_TEST_REPORT = new TestReport(0, 0, 0, EMPTY_STRING, + Collections.emptyList()); + private int failCount; private int skipCount; private int totalCount; private String urlName; + private TestReport(int failCount, int skipCount, int totalCount, String urlName, + List childReports) { + super(); + this.failCount = failCount; + this.skipCount = skipCount; + this.totalCount = totalCount; + this.urlName = urlName; + this.childReports = childReports; + } + + public TestReport() { + this.failCount = 0; + this.skipCount = 0; + this.totalCount = 0; + // FIXME: What is the best choice to initialize? + this.urlName = EMPTY_STRING; + } + private List childReports; public int getFailCount() { return failCount; } - public void setFailCount(int failCount) { + public TestReport setFailCount(int failCount) { this.failCount = failCount; + return this; } public int getSkipCount() { return skipCount; } - public void setSkipCount(int skipCount) { + public TestReport setSkipCount(int skipCount) { this.skipCount = skipCount; + return this; } public int getTotalCount() { return totalCount; } - public void setTotalCount(int totalCount) { + public TestReport setTotalCount(int totalCount) { this.totalCount = totalCount; + return this; } public String getUrlName() { return urlName; } - public void setUrlName(String urlName) { + public TestReport setUrlName(String urlName) { this.urlName = urlName; + return this; } public List getChildReports() { return childReports; } - public void setChildReports(List childReports) { + public TestReport setChildReports(List childReports) { this.childReports = childReports; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestResult.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestResult.java index d4b6f9e4..5dda306f 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestResult.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestResult.java @@ -6,7 +6,7 @@ * @author Karl Heinz Marbaise * */ -public class TestResult { +public class TestResult extends BaseModel { private double duration; private boolean empty; @@ -20,40 +20,54 @@ public double getDuration() { return duration; } - public void setDuration(double duration) { + public TestResult setDuration(double duration) { this.duration = duration; + return this; } public boolean isEmpty() { return empty; } - public void setEmpty(boolean empty) { + public TestResult setEmpty(boolean empty) { this.empty = empty; + return this; } public int getFailCount() { return failCount; } - public void setFailCount(int failCount) { + public TestResult setFailCount(int failCount) { this.failCount = failCount; + return this; } public int getPassCount() { return passCount; } - public void setPassCount(int passCount) { + public TestResult setPassCount(int passCount) { this.passCount = passCount; + return this; } public int getSkipCount() { return skipCount; } - public void setSkipCount(int skipCount) { + public TestResult setSkipCount(int skipCount) { this.skipCount = skipCount; + return this; + } + + public List getSuites() { + return suites; + } + + public TestResult setSuites(List suites) { + this.suites = suites; + return this; } @Override @@ -97,12 +111,4 @@ public boolean equals(Object obj) { return false; return true; } - - public List getSuites() { - return suites; - } - - public void setSuites(List suites) { - this.suites = suites; - } } diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestSuites.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestSuites.java index 45c96883..030b03b9 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestSuites.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/TestSuites.java @@ -19,40 +19,45 @@ public double getDuration() { return duration; } - public void setDuration(double duration) { + public TestSuites setDuration(double duration) { this.duration = duration; + return this; } public String getId() { return id; } - public void setId(String id) { + public TestSuites setId(String id) { this.id = id; + return this; } public String getName() { return name; } - public void setName(String name) { + public TestSuites setName(String name) { this.name = name; + return this; } public String getTimestamp() { return timestamp; } - public void setTimestamp(String timestamp) { + public TestSuites setTimestamp(String timestamp) { this.timestamp = timestamp; + return this; } public List getCases() { return cases; } - public void setCases(List cases) { + public TestSuites setCases(List cases) { this.cases = cases; + return this; } @Override diff --git a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/View.java b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/View.java index bac08fc3..422cd5a4 100644 --- a/jenkins-client/src/main/java/com/offbytwo/jenkins/model/View.java +++ b/jenkins-client/src/main/java/com/offbytwo/jenkins/model/View.java @@ -2,6 +2,9 @@ public class View extends MainView { + //TODO: Think about the initialization of + // the attributes? Better being done in + // ctor. private String name = ""; private String description = ""; private String url = ""; @@ -10,28 +13,30 @@ public String getName() { return name; } - public void setName(String name) { + public View setName(String name) { this.name = name; + return this; } public View() { - } public String getDescription() { return description; } - public void setDescription(String description) { + public View setDescription(String description) { this.description = description; + return this; } public String getUrl() { return url; } - public void setUrl(String url) { + public View setUrl(String url) { this.url = url; + return this; } @Override diff --git a/jenkins-client/src/site/asciidoc/index.adoc b/jenkins-client/src/site/asciidoc/index.adoc deleted file mode 100644 index 942df55c..00000000 --- a/jenkins-client/src/site/asciidoc/index.adoc +++ /dev/null @@ -1,7 +0,0 @@ -:revnumber: ${project-version} - -== Overview == - -Here you can see the first page. -This is the first. - diff --git a/jenkins-client/src/site/asciidoc/sub/sub.adoc b/jenkins-client/src/site/asciidoc/sub/sub.adoc deleted file mode 100644 index 345e6aef..00000000 --- a/jenkins-client/src/site/asciidoc/sub/sub.adoc +++ /dev/null @@ -1 +0,0 @@ -Test diff --git a/jenkins-client/src/site/site.xml b/jenkins-client/src/site/site.xml index 6ed3adec..e4138ea7 100644 --- a/jenkins-client/src/site/site.xml +++ b/jenkins-client/src/site/site.xml @@ -4,27 +4,10 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/DECORATION/1.0.0 http://maven.apache.org/xsd/decoration-1.0.0.xsd"> - - org.apache.maven.skins - maven-fluido-skin - 1.5 - - - - - true - true - - ${project.url} - - - RisingOak/jenkins-client - right - gray - - - + + + @@ -32,6 +15,5 @@ - diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/FolderManualTests.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/FolderManualTests.java deleted file mode 100644 index edcbafc7..00000000 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/FolderManualTests.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.offbytwo.jenkins; - -import java.net.URI; -import java.net.URISyntaxException; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import com.google.common.base.Optional; -import com.offbytwo.jenkins.model.FolderJob; -import com.offbytwo.jenkins.model.Job; -import com.offbytwo.jenkins.model.JobWithDetails; - -public class FolderManualTests { - - private JenkinsServer server; - - @Before - public void SetUp() throws URISyntaxException { - // point this at a local instance of jenkins which has the cloudbees - // folder plugin installed. - server = new JenkinsServer(new URI("http://localhost:8080/")); - } - - @Ignore - @Test - public void testFolderPluginAPIs() throws Exception { - server.createFolder("root"); - JobWithDetails root = server.getJob("root"); - Assert.assertNotNull(root); - - Optional rootFolder = server.getFolderJob(root); - Assert.assertTrue(rootFolder.isPresent()); - - server.createFolder(rootFolder.get(), "subfolder"); - JobWithDetails subfolder = server.getJob(rootFolder.get(), "subfolder"); - Assert.assertNotNull(subfolder); - - Optional subfolderFolder = server.getFolderJob(subfolder); - Assert.assertTrue(subfolderFolder.isPresent()); - - server.deleteJob("root"); - root = server.getJob("root"); - Assert.assertNull(root); - } - - @Ignore - @Test - public void testStuff() { - try { - - String f1 = "test-root"; - String f2 = "test-nested"; - String f3 = "test-double-nested"; - - server.createFolder(f1); - Job job1 = server.getJob(f1); - FolderJob fjob = server.getFolderJob(job1).get(); - server.createFolder(fjob, f2); - - Job job2 = server.getJob(fjob, f2); - FolderJob fjob2 = server.getFolderJob(job2).get(); - server.createFolder(fjob2, f3); - - Job job3 = server.getJob(fjob2, f3); - FolderJob fjob3 = server.getFolderJob(job3).get(); - - JobWithDetails dj = fjob3.details(); - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java index 2051c5ef..0c553ce1 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsServerTest.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; import org.apache.http.entity.ContentType; @@ -25,26 +26,26 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; -import com.google.common.base.Optional; import com.offbytwo.jenkins.client.JenkinsHttpClient; +import com.offbytwo.jenkins.client.JenkinsHttpConnection; import com.offbytwo.jenkins.model.FolderJob; import com.offbytwo.jenkins.model.Job; import com.offbytwo.jenkins.model.JobWithDetails; import com.offbytwo.jenkins.model.MainView; import com.offbytwo.jenkins.model.View; +import java.net.URI; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class JenkinsServerTest extends BaseUnitTest { - private JenkinsHttpClient client = mock(JenkinsHttpClient.class); + private JenkinsHttpConnection client = mock(JenkinsHttpClient.class); private JenkinsServer server = new JenkinsServer(client); private MainView mainView = new MainView(new Job("Hello", "http://localhost/job/Hello/")); - public JenkinsServerTest() throws Exception { - } - @Before public void setUp() throws Exception { given(client.get("/", MainView.class)).willReturn(mainView); @@ -73,8 +74,8 @@ public void testGetJobXml() throws Exception { @Test public void testFolderGetJobs() throws Exception { - - String[] jobNames = { "job-the-first", "Job-The-Next", "Job-the-Next"}; + + String[] jobNames = { "job-the-first", "Job-The-Next", "Job-the-Next" }; // given String path = "http://localhost/jobs/someFolder/"; Job someJob = new Job("jobname", path + "jobname"); @@ -84,19 +85,19 @@ public void testFolderGetJobs() throws Exception { MainView mv = createTestView(someJobs); given(client.get(eq(path), eq(MainView.class))).willReturn(mv); - + // when Map map = server.getJobs(folderJob); - + // then verify(client).get(path, MainView.class); - for(String name : jobNames) + for (String name : jobNames) assertTrue(someJobs.contains(map.get(name))); assertEquals(jobNames.length, map.size()); } - + @Test public void testFolderGetJob() throws Exception { // given @@ -158,7 +159,7 @@ public void testGetFolderJobInvalidFolder() throws Exception { given(folderJob.isFolder()).willReturn(false); given(client.get(eq(path), eq(FolderJob.class))).willReturn(folderJob); - + // when Optional oj = server.getFolderJob(someJob); @@ -190,7 +191,7 @@ public void testCreateSubFolderJob() throws Exception { // then verify(client).post_form(eq(path + "createItem?"), anyMap(), eq(false)); } - + @Test public void testUpdateJobXml() throws Exception { // given @@ -219,7 +220,7 @@ public void testCreateJob() throws Exception { // then ArgumentCaptor captureString = ArgumentCaptor.forClass(String.class); - verify(client).post_xml(eq("/createItem?name=" + jobName), captureString.capture(), eq(true)); + verify(client).post_xml(eq("/createItem?name=" + jobName), captureString.capture(), eq(false)); String xmlReturn = captureString.getValue(); assertEquals(xmlReturn, xmlString); } @@ -251,7 +252,7 @@ public void testQuietDown() throws IOException { @Test public void testScriptPosts() throws IOException, URISyntaxException { given(client.post_text("/scriptText", "script=script", ContentType.APPLICATION_FORM_URLENCODED, false)) - .willReturn("result"); + .willReturn("result"); String result = server.runScript("script"); verify(client).post_text("/scriptText", "script=script", ContentType.APPLICATION_FORM_URLENCODED, false); assertEquals("result", result); @@ -273,14 +274,13 @@ private MainView createTestView(String baseUrl, String... jobNames) { private List createTestJobs(String baseUrl, String... jobNames) { List jobs = new ArrayList(); - for(String name: jobNames) { - jobs.add(new Job(name, baseUrl+name)); + for (String name : jobNames) { + jobs.add(new Job(name, baseUrl + name)); } return jobs; } - @Test public void testReturnSingleJob() throws Exception { shouldReturnListOfJobs("hello"); @@ -296,16 +296,15 @@ public void testFolderGetSingleJob() throws Exception { shouldGetFolderJobs("jobname"); } - - private void shouldReturnListOfJobs(String...jobNames) throws Exception { + private void shouldReturnListOfJobs(String... jobNames) throws Exception { MainView mainView = createTestView("http://localhost/job/", jobNames); given(client.get("/", MainView.class)).willReturn(mainView); Map jobs = server.getJobs(); - for(String name : jobNames) + for (String name : jobNames) assertTrue(jobs.containsKey(name)); assertEquals(jobNames.length, jobs.size()); - } + } @Test public void testGetJobXmls() throws Exception { @@ -316,6 +315,35 @@ public void testGetJobXmls() throws Exception { shouldGetJobXml("HeLLo"); } + @Test + public void getVersionShouldNotFailWithNPE() + throws Exception + { + when (client.get( "/" )).thenReturn( "TheAnswer"); + when (client.getJenkinsVersion()).thenReturn( "1.23"); + + JenkinsServer server = new JenkinsServer( client); + server.getVersion(); + verify( client, times( 1 )).isJenkinsVersionSet(); + verify( client, times( 1 )).get( "/" ); + verify( client, times( 1 )).getJenkinsVersion(); + + } + + + @Test(expected=IllegalStateException.class) + public void testClose() throws Exception { + final String uri = "http://localhost/jenkins"; + JenkinsServer srv = new JenkinsServer(new URI(uri)); + srv.close(); + srv.close(); //check multiple calls yield no errors + srv.getComputers(); + } + + + + + private void shouldGetFolderJobs(String... jobNames) throws IOException { // given String path = "http://localhost/jobs/someFolder/"; @@ -325,18 +353,19 @@ private void shouldGetFolderJobs(String... jobNames) throws IOException { MainView mv = createTestView(someJobs); given(client.get(eq(path), eq(MainView.class))).willReturn(mv); - + // when Map map = server.getJobs(folderJob); - + // then verify(client).get(path, MainView.class); - for(String name : jobNames) + for (String name : jobNames) assertTrue(someJobs.contains(map.get(name))); assertEquals(jobNames.length, map.size()); } + private void shouldGetJobXml(String jobName) throws Exception { // given String xmlString = "some xml goes here"; @@ -347,8 +376,8 @@ private void shouldGetJobXml(String jobName) throws Exception { String xmlReturn = server.getJobXml(jobName); // then - verify(client).get("/job/"+jobName+"/config.xml"); - verify(client).get("/job/"+jobName+"/config.xml"); + verify(client).get("/job/" + jobName + "/config.xml"); + verify(client).get("/job/" + jobName + "/config.xml"); assertEquals(xmlString, xmlReturn); } } diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsTestManualTestReport.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsTestManualTestReport.java new file mode 100644 index 00000000..03db37db --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/JenkinsTestManualTestReport.java @@ -0,0 +1,56 @@ +package com.offbytwo.jenkins; + +import java.io.IOException; +import java.net.URI; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.junit.Ignore; +import org.junit.Test; + +import com.offbytwo.jenkins.model.Build; +import com.offbytwo.jenkins.model.JobWithDetails; +import com.offbytwo.jenkins.model.TestReport; +import com.offbytwo.jenkins.model.TestResult; +import com.offbytwo.jenkins.model.TestSuites; +import com.offbytwo.jenkins.model.View; + +public class JenkinsTestManualTestReport { + + @Ignore + public void firstTest() throws Exception { + + JenkinsServer js = new JenkinsServer(URI.create("http://localhost:10090/buildserver/"), "admin", "admin"); + JobWithDetails job = js.getJob("non-maven-test"); + Build lastCompletedBuild = job.getLastCompletedBuild(); + TestReport testReport = lastCompletedBuild.getTestReport(); + + System.out.println(" --- TestReport ---"); + System.out.println("totalCount: " + testReport.getTotalCount()); + System.out.println(" failCount: " + testReport.getFailCount()); + System.out.println(" skipCount: " + testReport.getSkipCount()); + + TestResult testResult = lastCompletedBuild.getTestResult(); + System.out.println(" --- TestResult ---"); + + System.out.println(" PassCount: " + testResult.getPassCount()); + System.out.println(" failCount: " + testResult.getFailCount()); + System.out.println(" skipCount: " + testResult.getSkipCount()); + System.out.println(" duration: " + testResult.getDuration()); + System.out.println(" isEmpty: " + testResult.isEmpty()); + List suites = testResult.getSuites(); + + } + + @Test + public void anotherTest() throws IOException { + JenkinsServer js = new JenkinsServer(URI.create("http://localhost:10090/buildserver/"), "admin", "admin"); + Map views = js.getViews(); + for (Entry item : views.entrySet()) { + View value = item.getValue(); + System.out.println("URL: " + value.getUrl()); + } + + } +} diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java new file mode 100644 index 00000000..2e3bce09 --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/JenkinsHttpClientTest.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.client; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +import java.io.ByteArrayInputStream; +import java.net.URI; +import org.apache.http.Header; +import org.apache.http.HttpEntity; +import org.apache.http.HttpStatus; +import org.apache.http.StatusLine; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.protocol.HttpContext; +import org.junit.Test; + + + +/** + * + * @author Dell Green + */ +public class JenkinsHttpClientTest { + private static final String URI = "http://localhost/jenkins"; + + + + @Test + public void testGet_String() throws Exception { + final CloseableHttpClient client = mock(CloseableHttpClient.class); + final CloseableHttpResponse response = mock(CloseableHttpResponse.class); + final Header versionHeader = mock(Header.class); + final StatusLine statusLine = mock(StatusLine.class); + final HttpEntity entity = mock(HttpEntity.class); + given(client.execute(any(HttpUriRequest.class), eq((HttpContext)null))).willReturn(response); + given(response.getHeaders("X-Jenkins")).willReturn(new Header[]{versionHeader}); + given(response.getStatusLine()).willReturn(statusLine); + given(versionHeader.getValue()).willReturn("1.234"); + given(statusLine.getStatusCode()).willReturn(HttpStatus.SC_OK); + given(response.getEntity()).willReturn(entity); + given(entity.getContent()).willReturn(new ByteArrayInputStream("someJson".getBytes())); + final JenkinsHttpConnection jclient = new JenkinsHttpClient(new URI(URI), client); + final String s = jclient.get("job/someJob"); + assertEquals("someJson", s); + } + + + + @Test(expected=IllegalStateException.class) + public void testClose() throws Exception { + final JenkinsHttpConnection jclient = new JenkinsHttpClient(new URI(URI)); + jclient.close(); + jclient.close(); //check multiple calls yield no errors + jclient.get("job/someJob"); + } + + +} \ No newline at end of file diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/EncodingUtilsTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/EncodingUtilsTest.java new file mode 100644 index 00000000..358ac7f5 --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/EncodingUtilsTest.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019 Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.client.util; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * This class is a unit test for the helper class {@link EncodingUtils}. + * + * @author Karl Heinz Marbaise + */ +public class EncodingUtilsTest { + + @Test + public void encodeShouldReturnEncodedDoubleQuoteAndSpace() { + String result = EncodingUtils.encode("!\"& "); + assertThat(result).isEqualTo("%21%22%26+"); + } + + @Test + public void encodeShouldReturnNotEncodeSafeChars() { + String result = EncodingUtils.encode("-._~!$'()*,;&=@:+"); + assertThat(result).isEqualTo("-._%7E%21%24%27%28%29*%2C%3B%26%3D%40%3A%2B"); + } + @Test + public void encodeShouldReturnNotEncodeAlpha() { + String result = EncodingUtils.encode("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + assertThat(result).isEqualTo("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"); + } + + @Test + public void encodeShouldReturnEncodingUmlautAndOthers() { + String result = EncodingUtils.encode("äöü#{}"); + assertThat(result).isEqualTo("%C3%A4%C3%B6%C3%BC%23%7B%7D"); + } + + @Test + public void encodeParamShouldReturnEncodedExclamationMarkDoubleQuoteAmpersampSpace() { + String result = EncodingUtils.formParameter("!\"& "); + assertThat(result).isEqualTo("%21%22%26+"); + } + + @Test + public void encodeParamShouldReturnNotEncodeSafeChars() { + String result = EncodingUtils.formParameter("-_.*"); + assertThat(result).isEqualTo("-_.*"); + } + + @Test + public void encodeParamShouldReturnEncodedUmlautAndOthers() { + String result = EncodingUtils.formParameter("äöü#{}"); + assertThat(result).isEqualTo("%C3%A4%C3%B6%C3%BC%23%7B%7D"); + } + + @Test + public void encodeParamShouldReturnEncodedCharacters() { + String result = EncodingUtils.formParameter("-._~!$'()*,;&=@:+"); + assertThat(result).isEqualTo("-._%7E%21%24%27%28%29*%2C%3B%26%3D%40%3A%2B"); + } + +} \ No newline at end of file diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/ResponseUtilsTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/ResponseUtilsTest.java new file mode 100644 index 00000000..57e001ea --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/ResponseUtilsTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.client.util; + +import org.apache.http.Header; +import org.apache.http.HttpResponse; +import static org.junit.Assert.assertEquals; +import org.junit.Test; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + + + +/** + * + * @author Dell Green + */ +public class ResponseUtilsTest { + + + + @Test + public void testGetJenkinsVersion() { + final Header header = mock(Header.class); + final HttpResponse response = mock(HttpResponse.class); + given(response.getHeaders("X-Jenkins")).willReturn(new Header[]{header}); + given(header.getValue()).willReturn("1.234"); + assertEquals("1.234", ResponseUtils.getJenkinsVersion(response)); + } + + + @Test + public void testGetJenkinsVersion_NoHeader() { + final HttpResponse response = mock(HttpResponse.class); + given(response.getHeaders("X-Jenkins")).willReturn(new Header[0]); + assertEquals("", ResponseUtils.getJenkinsVersion(response)); + } + + + @Test(expected = NullPointerException.class) + public void testGetJenkinsVersion_NullResponse() { + ResponseUtils.getJenkinsVersion(null); + } + + +} \ No newline at end of file diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/UrlUtilsTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/UrlUtilsTest.java new file mode 100644 index 00000000..780600ba --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/client/util/UrlUtilsTest.java @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2013 Cosmin Stejerean, Karl Heinz Marbaise, and contributors. + * + * Distributed under the MIT license: http://opensource.org/licenses/MIT + */ +package com.offbytwo.jenkins.client.util; + +import com.offbytwo.jenkins.model.FolderJob; +import java.net.URI; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import org.junit.Test; + + + +/** + * + * @author Dell Green + */ +public class UrlUtilsTest { + + + + @Test + public void testToBaseUrl_NullFolderJob() { + assertEquals("/", UrlUtils.toBaseUrl(null)); + } + + @Test + public void testToBaseUrl_DefaultFolderJob() { + assertNull("/", UrlUtils.toBaseUrl(new FolderJob())); + } + + + @Test + public void testToBaseUrl() { + final String fpath = "http://localhost/jobs/someFolder/"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + assertEquals(fpath, UrlUtils.toBaseUrl(folderJob)); + } + + + @Test + public void testToJobBaseUrl() { + final String fpath = "http://localhost/jobs/someFolder/"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + final String expected = "http://localhost/jobs/someFolder/job/someJob"; + assertEquals(expected, UrlUtils.toJobBaseUrl(folderJob, "someJob")); + } + + @Test + public void testToJobBaseUrl_NoTrailingFolderSlash() { + final String fpath = "http://localhost/jobs/someFolder"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + final String expected = "http://localhost/jobs/someFolder/job/someJob"; + assertEquals(expected, UrlUtils.toJobBaseUrl(folderJob, "someJob")); + } + + + @Test + public void testToJobBaseUrl_NullFolderJob() { + assertEquals("/job/someJob", UrlUtils.toJobBaseUrl(null, "someJob")); + } + + + @Test + public void testToJobBaseUrl_EmptyJobName() { + final String fpath = "http://localhost/jobs/someFolder"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + final String expected = "http://localhost/jobs/someFolder/job/"; + assertEquals(expected, UrlUtils.toJobBaseUrl(folderJob, "")); + } + + @Test(expected = NullPointerException.class) + public void testToJobBaseUrl_NullJobName() { + final String fpath = "http://localhost/jobs/someFolder"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + UrlUtils.toJobBaseUrl(folderJob, null); + } + + + @Test + public void testToViewBaseUrl() { + final String fpath = "http://localhost/jobs/someFolder/"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + final String expected = "http://localhost/jobs/someFolder/view/someView"; + assertEquals(expected, UrlUtils.toViewBaseUrl(folderJob, "someView")); + } + + @Test + public void testToViewBaseUrl_NoTrailingFolderSlash() { + final String fpath = "http://localhost/jobs/someFolder"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + final String expected = "http://localhost/jobs/someFolder/view/someView"; + assertEquals(expected, UrlUtils.toViewBaseUrl(folderJob, "someView")); + } + + + @Test + public void testToViewBaseUrl_NullFolderJob() { + assertEquals("/view/someView", UrlUtils.toViewBaseUrl(null, "someView")); + } + + + @Test + public void testToViewBaseUrl_EmptyViewName() { + final String fpath = "http://localhost/jobs/someFolder"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + final String expected = "http://localhost/jobs/someFolder/view/"; + assertEquals(expected, UrlUtils.toViewBaseUrl(folderJob, "")); + } + + @Test(expected = NullPointerException.class) + public void testToViewBaseUrl_NullViewName() { + final String fpath = "http://localhost/jobs/someFolder"; + final FolderJob folderJob = new FolderJob("someFolder", fpath); + UrlUtils.toViewBaseUrl(folderJob, null); + } + + + @Test + public void testToFullJobPath_JobNameOnly() { + assertEquals("someJob", UrlUtils.toFullJobPath("someJob")); + } + + + @Test + public void testToFullJobPath_JobNameWithSingleFolder() { + final String expected = "someFolder/job/someJob"; + assertEquals(expected, UrlUtils.toFullJobPath("someFolder/someJob")); + } + + @Test + public void testToFullJobPath_JobNameWithMultipleFolders() { + final String expected = "someFolder1/job/someFolder2/job/someJob"; + assertEquals(expected, UrlUtils.toFullJobPath("someFolder1/someFolder2/someJob")); + } + + @Test(expected = NullPointerException.class) + public void testToFullJobPath_NullJobName() { + UrlUtils.toFullJobPath(null); + } + + + @Test + public void testToFullJobPath_EmptyJobName() { + assertEquals("", UrlUtils.toFullJobPath("")); + } + + + @Test + public void testJoin_EmptyBothPaths() { + assertEquals("", UrlUtils.join("", "")); + } + + @Test + public void testJoin_SlashesOnly() { + assertEquals("/", UrlUtils.join("/", "/")); + } + + @Test + public void testJoin_EmptyPath2() { + assertEquals("1/2/3", UrlUtils.join("1/2/3", "")); + } + + @Test + public void testJoin_EmptyPath1() { + assertEquals("4/5/6", UrlUtils.join("", "4/5/6")); + } + + @Test + public void testJoin_NoTrailingLeadingSlashes() { + assertEquals("1/2/3/4/5/6", UrlUtils.join("1/2/3", "4/5/6")); + + } + + @Test + public void testJoin_TrailingLeadingSlashes() { + assertEquals("/1/2/3/4/5/6/", UrlUtils.join("/1/2/3/", "/4/5/6/")); + } + + @Test + public void testJoin_Path1Trailing_Path2NoLeading() { + assertEquals("/1/2/3/4/5/6/", UrlUtils.join("/1/2/3/", "4/5/6/")); + } + + @Test + public void testJoin_Path1NoTrailing_Path2Leading() { + assertEquals("/1/2/3/4/5/6/", UrlUtils.join("/1/2/3", "/4/5/6/")); + } + + + + + + @Test(expected = NullPointerException.class) + public void testJoin_NullPath1() { + UrlUtils.join(null, "4/5/6"); + } + + @Test(expected = NullPointerException.class) + public void testJoin_NullPath2() { + UrlUtils.join("1/2/3", null); + } + + + @Test + public void testToQueryUri_WithoutQuery() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "jenkins", "job/somejob"); + final String expected = "http://localhost/jenkins/job/somejob/api/json"; + assertEquals(expected, uri.toString()); + } + + @Test + public void testToQueryUri_EmptyContext() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "", "job/somejob"); + final String expected = "http://localhost/job/somejob/api/json"; + assertEquals(expected, uri.toString()); + } + + @Test + public void testToQueryUri_EmptyPathAndContext() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "", ""); + final String expected = "http://localhost/api/json"; + assertEquals(expected, uri.toString()); + } + + @Test(expected = NullPointerException.class) + public void testToQueryUri_NullUri() throws Exception { + UrlUtils.toJsonApiUri(null, "jenkins", "job/somejob"); + } + + + @Test(expected = NullPointerException.class) + public void testToQueryUri_NullPath() throws Exception { + UrlUtils.toJsonApiUri(new URI("http://localhost/jenkins"), "jenkins", null); + } + + @Test(expected = NullPointerException.class) + public void testToQueryUri_NullContext() throws Exception { + UrlUtils.toJsonApiUri(new URI("http://localhost/jenkins"), null, "job/ajob"); + } + + @Test + public void testToQueryUri_EmptyPath() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "jenkins", ""); + final String expected = "http://localhost/jenkins/api/json"; + assertEquals(expected, uri.toString()); + } + + @Test + public void testToQueryUri_UpperCase() throws Exception { + final String suri = "HTTP://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "jenkins", "job/somejob"); + final String expected = "HTTP://localhost/jenkins/job/somejob/api/json"; + assertEquals(expected, uri.toString()); + } + + + @Test + public void testToQueryUri_WithQuery() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "jenkins", "job/somejob?pretty=true"); + final String expected = "http://localhost/jenkins/job/somejob/api/json?pretty=true"; + assertEquals(expected, uri.toString()); + } + + + + @Test + public void testToQueryUri_PathContainsSchemeAndContext() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toJsonApiUri(new URI(suri), "jenkins", "http://localhost/jenkins/job/somejob"); + final String expected = "http://localhost/jenkins/job/somejob/api/json"; + assertEquals(expected, uri.toString()); + } + + + @Test + public void testToNoQueryUri_PathContainsSchemeAndContext() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toNoApiUri(new URI(suri), "jenkins", "http://localhost/jenkins/job/somejob"); + final String expected = "http://localhost/jenkins/job/somejob"; + assertEquals(expected, uri.toString()); + } + + + @Test + public void testToNoQueryUri_UpperCase() throws Exception { + final String suri = "HTTP://localhost/jenkins"; + final URI uri = UrlUtils.toNoApiUri(new URI(suri), "jenkins", "job/somejob"); + final String expected = "HTTP://localhost/jenkins/job/somejob"; + assertEquals(expected, uri.toString()); + } + + + @Test + public void testToNoQueryUri_EmptyPath() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toNoApiUri(new URI(suri), "jenkins", ""); + final String expected = "http://localhost/jenkins"; + assertEquals(expected, uri.toString()); + } + + + @Test + public void testToNoQueryUri_EmptyContext() throws Exception { + final String suri = "http://localhost/jenkins"; + final URI uri = UrlUtils.toNoApiUri(new URI(suri), "", "job/somejob"); + final String expected = "http://localhost/job/somejob"; + assertEquals(expected, uri.toString()); + } + + + @Test(expected = NullPointerException.class) + public void testToNoQueryUri_NullContext() throws Exception { + UrlUtils.toNoApiUri(new URI("http://localhost/jenkins"), null, "job/ajob"); + } + +} \ No newline at end of file diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/JenkinsVersionTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/JenkinsVersionTest.java new file mode 100644 index 00000000..e55a24ae --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/JenkinsVersionTest.java @@ -0,0 +1,139 @@ +package com.offbytwo.jenkins.helper; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; + +public class JenkinsVersionTest { + + @Test + public void isGreaterThanTrue() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isGreaterThan("1.548")).isTrue(); + } + + @Test + public void isGreaterThanFalse() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isGreaterThan("1.651.1")).isFalse(); + } + + @Test + public void isGreaterThanTrueJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.548"); + assertThat(a.isGreaterThan(b)).isTrue(); + } + + @Test + public void isGreaterThanFalseJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.1"); + assertThat(a.isGreaterThan(b)).isFalse(); + } + + @Test + public void isEqualToTrue() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isEqualTo("1.651.1")).isTrue(); + } + + @Test + public void isEqualToFalse() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isEqualTo("1.651.0")).isFalse(); + } + + @Test + public void isEqualToTrueJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.1"); + assertThat(a.isEqualTo(b)).isTrue(); + } + + @Test + public void isEqualToFalseJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.0"); + assertThat(a.isEqualTo(b)).isFalse(); + } + + @Test + public void isGreaterOrEqualTrue() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isGreaterOrEqual("1.651.1")).isTrue(); + } + + @Test + public void isGreaterOrEqualFalse() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isGreaterOrEqual("1.651.2")).isFalse(); + } + + @Test + public void isGreaterOrEqualTrueJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.1"); + assertThat(a.isGreaterOrEqual(b)).isTrue(); + } + + @Test + public void isGreaterOrEqualFalseJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.2"); + assertThat(a.isGreaterOrEqual(b)).isFalse(); + } + + @Test + public void isLessOrEqualTrue() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isLessOrEqual("1.651.1")).isTrue(); + } + + @Test + public void isLessOrEqualFalse() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isLessOrEqual("1.651.0")).isFalse(); + } + + @Test + public void isLessOrEqualTrueJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.1"); + assertThat(a.isLessOrEqual(b)).isTrue(); + } + + @Test + public void isLessOrEqualFalseJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.0"); + assertThat(a.isLessOrEqual(b)).isFalse(); + } + + @Test + public void isLessThanTrue() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isLessThan("1.651.2")).isTrue(); + } + + @Test + public void isLessThanFalse() { + JenkinsVersion jv = new JenkinsVersion("1.651.1"); + assertThat(jv.isLessThan("1.651.1")).isFalse(); + } + + @Test + public void isLessThanTrueJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.2"); + assertThat(a.isLessThan(b)).isTrue(); + } + + @Test + public void isLessThanFalseJenkinsVersion() { + JenkinsVersion a = new JenkinsVersion("1.651.1"); + JenkinsVersion b = new JenkinsVersion("1.651.1"); + assertThat(a.isLessThan(b)).isFalse(); + } + +} diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/RangeTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/RangeTest.java index 095ff292..b56aa467 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/RangeTest.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/helper/RangeTest.java @@ -6,13 +6,10 @@ package com.offbytwo.jenkins.helper; -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import com.offbytwo.jenkins.helper.Range; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; /** *
      @@ -23,57 +20,57 @@ * The same as {0,N}. *
    • {N}: Just retrieve the N-th element. The same as {N,N+1}.
    • *
    - * - * @author Karl Heinz Marbaise * + * @author Karl Heinz Marbaise */ public class RangeTest { + private String getEscaped(String m) { + return Range.CURLY_BRACKET_OPEN + m + Range.CURLY_BRACKET_CLOSE; + } + @Test public void fromToGiven() { Range r = Range.build().from(1).to(5); - assertThat(r.getRangeString()).isEqualTo("{1,5}"); + assertThat(r.getRangeString()).isEqualTo(getEscaped("1,5")); } - + @Test public void onlyFromGiven() { Range r = Range.build().from(3).build(); - assertThat(r.getRangeString()).isEqualTo("{3,}"); + assertThat(r.getRangeString()).isEqualTo(getEscaped("3,")); } @Test public void onlyToGiven() { Range r = Range.build().to(5).build(); - assertThat(r.getRangeString()).isEqualTo("{,5}"); + assertThat(r.getRangeString()).isEqualTo(getEscaped(",5")); } @Test public void onlyGiven() { Range r = Range.build().only(3); - assertThat(r.getRangeString()).isEqualTo("{3,4}"); + assertThat(r.getRangeString()).isEqualTo(getEscaped("3,4")); } - @Rule - public ExpectedException exception = ExpectedException.none(); - @Test public void toIsGivenLargerThanFromShouldResultInIllegalArgumentException() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("to must be greater than from"); - Range.build().from(5).to(1); + assertThatIllegalArgumentException() + .isThrownBy(() -> Range.build().from(5).to(1)) + .withMessage("to must be greater than from"); } @Test public void fromGivenNegativeValueShouldResultInIllegalArgumentException() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("from value must be greater or equal null."); - Range.build().from(-1); + assertThatIllegalArgumentException() + .isThrownBy(() -> Range.build().from(-1)) + .withMessage("from value must be greater or equal null."); } @Test public void fromGivenPositiveToNegativeValueShouldResultInIllegalArgumentException() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("to must be greater or equal null."); - Range.build().from(5).to(-1); + assertThatIllegalArgumentException() + .isThrownBy(() -> Range.build().from(5).to(-1)) + .withMessage("to must be greater or equal null."); } } diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/BuildJobTestReports.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/BuildJobTestReports.java index 62228f0b..9a284b6c 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/BuildJobTestReports.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/BuildJobTestReports.java @@ -21,16 +21,16 @@ public class BuildJobTestReports { @Test public void shouldAddStringParamToAnExistingJob() throws IOException { JenkinsServer js = new - JenkinsServer(URI.create("http://localhost:10090/")); + JenkinsServer(URI.create("http://localhost:10090/buildserver/"), "admin", "admin"); // JenkinsServer js = new JenkinsServer(URI.create("http://ci.soebes.de:8080/")); // MavenJobWithDetails mavenJob = js.getMavenJob("javaee"); - MavenJobWithDetails mavenJob = js.getMavenJob("test-maven"); + MavenJobWithDetails mavenJob = js.getMavenJob("maven-test"); BuildWithDetails details = mavenJob.getLastSuccessfulBuild().details(); // BuildWithDetails details = mavenJob.getBuilds().get(8).details(); System.out.println("Build Number: " + details.getNumber()); - TestReport testReport = mavenJob.getLastUnstableBuild().getTestReport(); + TestReport testReport = mavenJob.getLastBuild().getTestReport(); System.out.println("------ Tests"); System.out.println(" urlName: " + testReport.getUrlName()); System.out.println(" failCount: " + testReport.getFailCount()); diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/ComputerSetIT.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/ComputerSetIT.java index e83a31d5..b2be9010 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/ComputerSetIT.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/ComputerSetIT.java @@ -32,7 +32,7 @@ public void shouldGetTotalExecutors() throws JAXBException, IOException, Documen public void shouldGetComputerWithDetailsAndExecutors() throws IOException { Jenkins ji = jenkinsRule.getInstance(); - List computerSet = jenkinsServer.getComputerSet().getComputer(); + List computerSet = jenkinsServer.getComputerSet().getComputers(); ComputerWithDetails computerWithDetails = computerSet.get(0); assertThat(computerWithDetails.getExecutors()).isNotNull(); assertThat(computerWithDetails.getNumExecutors()).isEqualTo(ji.getNumExecutors()); @@ -41,10 +41,10 @@ public void shouldGetComputerWithDetailsAndExecutors() throws IOException { @Test public void shouldTrunFromOnlineToOffline() throws IOException { - ComputerWithDetails computerWithDetails = jenkinsServer.getComputerSet().getComputer().get( 0 ); + ComputerWithDetails computerWithDetails = jenkinsServer.getComputerSet().getComputers().get( 0 ); computerWithDetails.toggleOffline(true); - ComputerWithDetails computerWithDetailsAfterStarting = jenkinsServer.getComputerSet().getComputer().get( 0 ); + ComputerWithDetails computerWithDetailsAfterStarting = jenkinsServer.getComputerSet().getComputers().get( 0 ); assertThat( computerWithDetailsAfterStarting.getOffline() ).isTrue(); } diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/FolderTestsIT.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/FolderTestsIT.java index c6549413..098102c0 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/FolderTestsIT.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/FolderTestsIT.java @@ -5,6 +5,7 @@ import hudson.model.UpdateCenter; import java.util.List; +import java.util.Optional; import java.util.Vector; import jenkins.model.Jenkins; @@ -19,7 +20,6 @@ import org.kohsuke.stapler.StaplerResponse; import org.mockito.Mockito; -import com.google.common.base.Optional; import com.offbytwo.jenkins.JenkinsServer; import com.offbytwo.jenkins.model.FolderJob; import com.offbytwo.jenkins.model.JobWithDetails; @@ -67,7 +67,7 @@ public void testFolderPluginAPIs() throws Exception { JobWithDetails root = server.getJob("root"); Assert.assertNotNull(root); - Optional rootFolder = server.getFolderJob(root); + java.util.Optional rootFolder = server.getFolderJob(root); Assert.assertTrue(rootFolder.isPresent()); server.createFolder(rootFolder.get(), "subfolder"); diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsClientViewIT.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsClientViewIT.java index bd3f25ec..4581fb42 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsClientViewIT.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsClientViewIT.java @@ -12,6 +12,7 @@ import javax.xml.bind.JAXBException; import org.dom4j.DocumentException; +import org.junit.Ignore; import org.junit.Test; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; @@ -28,6 +29,7 @@ public class JenkinsClientViewIT extends BaseForIntegrationTests { public static final String TEST_VIEW = "testView"; @Test + @Ignore("I need to check what real cause of this is: JenkinsClientViewIT.shouldObtainView:36 » NullPointer") public void shouldObtainView() throws URISyntaxException, IOException, JAXBException, DocumentException { // given jenkinsRule.getInstance().addView(new TestView()); diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java index 6815db03..b32d027b 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/integration/JenkinsServerIT.java @@ -88,7 +88,7 @@ public void shouldSupportBooleanParameters() throws Exception { JobWithDetails job = server.getJobs().get(JENKINS_TEST_JOB).details(); BuildWithDetails build = job.getBuilds().get(0).details(); - assertEquals("true", build.getParameters().get("someValue")); + assertEquals(Boolean.TRUE, build.getParameters().get("someValue")); } @Test @@ -141,7 +141,7 @@ public void testCreateJob() throws Exception { jenkinsRule.getInstance().createProject(FreeStyleProject.class, JENKINS_TEST_JOB); String sourceXml = server.getJobXml(JENKINS_TEST_JOB); - server.createJob(jobName, sourceXml); + server.createJob(jobName, sourceXml, true); Map jobs = server.getJobs(); assertTrue(jobs.containsKey(jobName)); @@ -183,4 +183,26 @@ public void testUpdateJob() throws Exception { String confirmXml = server.getJobXml(JENKINS_TEST_JOB); assertTrue(confirmXml.contains(description)); } + + + @Test(expected=IllegalStateException.class) + public void testClose_ReuseAfterClosed() throws Exception { + final FreeStyleProject proj = jenkinsRule.getInstance().createProject( + FreeStyleProject.class, JENKINS_TEST_JOB); + final Map jobs = server.getJobs(); + assertNotNull(jobs.get(proj.getName())); + server.close(); + server.getJobs(); + } + + + @Test + public void testClose_CloseMultipleTimes() throws Exception { + final FreeStyleProject proj = jenkinsRule.getInstance().createProject( + FreeStyleProject.class, JENKINS_TEST_JOB); + final Map jobs = server.getJobs(); + assertNotNull(jobs.get(proj.getName())); + server.close(); + server.close(); + } } diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/model/BuildWithDetailsTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/model/BuildWithDetailsTest.java new file mode 100644 index 00000000..dd4969b3 --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/model/BuildWithDetailsTest.java @@ -0,0 +1,123 @@ +package com.offbytwo.jenkins.model; + +import com.offbytwo.jenkins.BaseUnitTest; +import com.offbytwo.jenkins.client.JenkinsHttpClient; +import com.offbytwo.jenkins.client.JenkinsHttpConnection; +import com.offbytwo.jenkins.helper.BuildConsoleStreamListener; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.entity.StringEntity; +import org.apache.http.message.BasicHeader; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.BDDMockito.given; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyListOf; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; + +/** + */ +public class BuildWithDetailsTest extends BaseUnitTest { + + private JenkinsHttpConnection client; + private BuildWithDetails buildWithDetails; + + @Before + public void setUp() { + client = mock(JenkinsHttpClient.class); + buildWithDetails = givenBuild(); + } + + private BuildWithDetails givenBuild() { + BuildWithDetails buildWithDetails = new BuildWithDetails(); + buildWithDetails.setClient(client); + return buildWithDetails; + } + + @Test + public void getBuildLogWithBuffer() { + try { + HttpResponse response = mock(HttpResponse.class); + String text = "test test test"; + int textLength = text.length(); + given(response.getEntity()).willReturn(new StringEntity(text)); + BasicHeader moreDataHeader = new BasicHeader(BuildWithDetails.MORE_DATA_HEADER, "false"); + BasicHeader textSizeHeader = new BasicHeader(BuildWithDetails.TEXT_SIZE_HEADER, Integer.toString(textLength)); + given(response.getFirstHeader(BuildWithDetails.MORE_DATA_HEADER)).willReturn(moreDataHeader); + given(response.getFirstHeader(BuildWithDetails.TEXT_SIZE_HEADER)).willReturn(textSizeHeader); + given(client.post_form_with_result(anyString(),anyListOf(NameValuePair.class),anyBoolean())).willReturn(response); + ConsoleLog consoleOutputText = buildWithDetails.getConsoleOutputText(500, false); + assertThat(consoleOutputText.getConsoleLog()).isEqualTo(text); + assertThat(consoleOutputText.getCurrentBufferSize()).isEqualTo(textLength); + assertThat(consoleOutputText.getHasMoreData()).isFalse(); + } catch (IOException e) { + fail("Should not return exception",e); + } + } + + + @Test + public void poolBuildLog() throws InterruptedException { + try { + HttpResponse response = mock(HttpResponse.class); + final String text = "test test test"; + int textLength = text.length(); + given(response.getEntity()).willReturn(new StringEntity(text)); + BasicHeader moreDataHeader = new BasicHeader(BuildWithDetails.MORE_DATA_HEADER, "false"); + BasicHeader textSizeHeader = new BasicHeader(BuildWithDetails.TEXT_SIZE_HEADER, Integer.toString(textLength)); + given(response.getFirstHeader(BuildWithDetails.MORE_DATA_HEADER)).willReturn(moreDataHeader); + given(response.getFirstHeader(BuildWithDetails.TEXT_SIZE_HEADER)).willReturn(textSizeHeader); + given(client.post_form_with_result(anyString(),anyListOf(NameValuePair.class),anyBoolean())).willReturn(response); + final StringBuffer buffer=new StringBuffer(); + buildWithDetails.streamConsoleOutput(new BuildConsoleStreamListener() { + @Override + public void onData(String newLogChunk) { + assertThat(newLogChunk).isEqualTo(text); + buffer.append(text); + } + + @Override + public void finished() { + assertThat(buffer.toString()).isEqualTo(text); + } + },1,2, false); + } catch (IOException e) { + fail("Should not return exception",e); + } + } + + @Test + public void poolBuildLogShouldTimeout() throws InterruptedException { + try { + HttpResponse response = mock(HttpResponse.class); + final String text = "test test test"; + int textLength = text.length(); + given(response.getEntity()).willReturn(new StringEntity(text)); + BasicHeader moreDataHeader = new BasicHeader(BuildWithDetails.MORE_DATA_HEADER, "true"); + BasicHeader textSizeHeader = new BasicHeader(BuildWithDetails.TEXT_SIZE_HEADER, Integer.toString(textLength)); + given(response.getFirstHeader(BuildWithDetails.MORE_DATA_HEADER)).willReturn(moreDataHeader); + given(response.getFirstHeader(BuildWithDetails.TEXT_SIZE_HEADER)).willReturn(textSizeHeader); + given(client.post_form_with_result(anyString(),anyListOf(NameValuePair.class),anyBoolean())).willReturn(response); + buildWithDetails.streamConsoleOutput(new BuildConsoleStreamListener() { + @Override + public void onData(String newLogChunk) { + assertThat(newLogChunk).isEqualTo(text); + } + + @Override + public void finished() { + fail("Should timeout"); + } + },1,2, false); + } catch (IOException e) { + fail("Should not return exception",e); + } + } + +} diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/model/ChangeSetTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/model/ChangeSetTest.java new file mode 100644 index 00000000..b1e7ae33 --- /dev/null +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/model/ChangeSetTest.java @@ -0,0 +1,74 @@ +package com.offbytwo.jenkins.model; + +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; +import static org.junit.Assert.*; + + +import org.junit.Test; + + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class ChangeSetTest { + + private BuildWithDetails getBuildFromJson(String json) throws Exception { + ObjectMapper mapper = new ObjectMapper(); + mapper.disable(FAIL_ON_UNKNOWN_PROPERTIES); + return mapper.readValue(json, BuildWithDetails.class); + } + + final String changeSetExampleJson = " {" + + " \"_class\" : \"hudson.plugins.git.GitChangeSetList\"," + + " \"items\" : [" + + " {" + + " \"_class\" : \"hudson.plugins.git.GitChangeSet\"," + + " \"affectedPaths\" : [" + + " \"README.md\"" + + " ]," + + " \"commitId\" : \"ba40ff32c60f692918c1d51f5c80842124ed04af\"," + + " \"timestamp\" : 1519306808000," + + " \"author\" : {" + + " \"absoluteUrl\" : \"https://my.jenkins/user/john.doe\"," + + " \"fullName\" : \"john.doe\"" + + " }," + + " \"authorEmail\" : \"john.doe@flap.com\"," + + + " \"comment\" : \"longer\\ncommit message\\n\"," + + " \"date\" : \"2018-02-22 13:40:08 +0000\"," + + " \"id\" : \"ba40ff32c60f692918c1d51f5c80842124ed04af\"," + + " \"msg\" : \"longer\"," + + " \"paths\" : [" + + " {" + + " \"editType\" : \"edit\"," + + " \"file\" : \"README.md\"" + + " }" + + " ]" + + " }" + + " ]," + + " \"kind\" : \"git\"" + + " }"; + + + @Test + public void getChangeSet__forBuildWithChangeSetAsSingleObject() throws Exception { + String json = String.format("{ \"changeSet\" : %s }", changeSetExampleJson); + + BuildWithDetails examinee = getBuildFromJson(json); + + BuildChangeSetItem item = examinee.getChangeSet().getItems().get(0); + assertEquals(item.getAuthor().getFullName(), "john.doe"); + + } + + @Test + public void getChangeSet__forBuildWithChangeSetsAsList() throws Exception { + String json = String.format("{ \"changeSets\" : [ %s ] }", changeSetExampleJson); + + BuildWithDetails examinee = getBuildFromJson(json); + + BuildChangeSetItem item = examinee.getChangeSet().getItems().get(0); + assertEquals(item.getAuthor().getFullName(), "john.doe"); + + assertEquals(examinee.getChangeSets().size(), 1); + } +} diff --git a/jenkins-client/src/test/java/com/offbytwo/jenkins/model/JobWithDetailsTest.java b/jenkins-client/src/test/java/com/offbytwo/jenkins/model/JobWithDetailsTest.java index 20d7e57c..36af7c96 100644 --- a/jenkins-client/src/test/java/com/offbytwo/jenkins/model/JobWithDetailsTest.java +++ b/jenkins-client/src/test/java/com/offbytwo/jenkins/model/JobWithDetailsTest.java @@ -21,7 +21,7 @@ private JobWithDetails givenNewJobWithoutAnyBuilds() { } private void failIfNotBuildHasNeverRanReturned(Build build) { - assertThat(build).isEqualTo(Build.BUILD_HAS_NEVER_RAN); + assertThat(build).isEqualTo(Build.BUILD_HAS_NEVER_RUN); } @Test diff --git a/pom.xml b/pom.xml index e390c86f..f88539d9 100644 --- a/pom.xml +++ b/pom.xml @@ -15,59 +15,59 @@ Jenkins API client for Java - http://github.com/RisingOak/jenkins-client + http://github.com/jenkinsci/java-client-api A Jenkins API client for Java com.offbytwo.jenkins jenkins-client-parent - 0.3.6-SNAPSHOT + 0.4.0-SNAPSHOT pom - scm:git:git@github.com:RisingOak/jenkins-client.git - scm:git:git@github.com:RisingOak/jenkins-client.git - https://github.com/RisingOak/jenkins-client + scm:git:https://github.com/jenkinsci/java-client-api.git + scm:git:ssh://git@github.com/jenkinsci/java-client-api.git + https://github.com/jenkinsci/java-client-api HEAD github - scm:git:git@github.com:RisingOak/jenkins-client.git + scm:git:ssh://git@github.com/jenkinsci/java-client-api.git Travis-CI - https://travis-ci.org/RisingOak/jenkins-client/ + https://travis-ci.org/jenkinsci/java-client-api/ UTF-8 UTF-8 - 1.7 - 1.7 + 1.8 + 1.8 true true - 1.5.3 + 2.0.0 1.644 - 4.11 - 1.9.5 + 4.12 + 3.0.0 2.4 1.4.7-jenkins-1 - 1.6.1 - 2.6 - 17.0 + 3.9 2.4 - 4.3.6 - 2.3.4 + 4.5.8 + 4.4.11 + 4.5.8 + 2.10.3 MIT License - http://opensource.org/licenses/MIT + https://opensource.org/licenses/MIT/ @@ -104,9 +104,9 @@ - dom4j + org.dom4j dom4j - ${dom4j.version} + 2.1.3 @@ -125,14 +125,8 @@ - com.google.guava - guava - ${guava.version} - - - - commons-lang - commons-lang + org.apache.commons + commons-lang3 ${commons-lang.version} @@ -148,6 +142,18 @@ ${httpclient.version} + + org.apache.httpcomponents + httpcore + ${httpcore.version} + + + + org.apache.httpcomponents + httpmime + ${httpmime.version} + + jaxen jaxen @@ -158,55 +164,54 @@ org.testng testng - 6.8.21 - test + 7.0.0 + + + org.junit + junit-bom + 5.5.2 + import + pom junit junit ${junit.version} - test org.mockito mockito-core ${mockito-core.version} - test org.jenkins-ci.main jenkins-test-harness ${jenkins-version} - test org.assertj assertj-core - 1.7.1 - test + 3.12.2 - + org.slf4j slf4j-api - 1.7.21 + 1.7.25 org.apache.logging.log4j - log4j-core - 2.5 + log4j-bom + 2.12.1 + import + pom - org.apache.logging.log4j - log4j-slf4j-impl - 2.5 - - - xml-apis - xml-apis - 1.4.01 + xerces + xmlParserAPIs + 2.6.1 @@ -214,20 +219,31 @@ + + org.apache.maven.plugins + maven-help-plugin + 3.2.0 + + + org.apache.maven.plugins + maven-clean-plugin + 3.1.0 + org.apache.maven.plugins maven-resources-plugin - 2.7 + 3.1.0 org.apache.maven.plugins maven-source-plugin - 3.0.0 + 3.1.0 attach-sources @@ -238,22 +254,27 @@ org.apache.maven.plugins maven-surefire-plugin - 2.18.1 + 2.22.2 org.apache.maven.plugins maven-failsafe-plugin - 2.18.1 + 2.22.2 + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.22.2 org.apache.maven.plugins maven-shade-plugin - 2.4.1 + 3.2.1 org.apache.maven.plugins maven-jar-plugin - 2.6 + 3.1.2 @@ -263,11 +284,11 @@ - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + @@ -279,12 +300,17 @@ org.apache.maven.plugins maven-deploy-plugin - 2.8.2 + 3.0.0-M1 + + + org.apache.maven.plugins + maven-install-plugin + 3.0.0-M1 org.apache.maven.plugins maven-release-plugin - 2.5.2 + 2.5.3 - - font - coderay - style - 2 - ${project.version} - true - true - - - - + + org.apache.maven.plugins + maven-site-plugin + + + org.apache.maven.doxia + doxia-module-markdown + 1.9 + + + @@ -351,7 +372,6 @@ org.apache.maven.plugins maven-surefire-report-plugin - 2.18.1 @@ -364,33 +384,19 @@ org.apache.maven.plugins maven-project-info-reports-plugin - 2.8 - @@ -434,12 +440,12 @@ org.apache.maven.plugins maven-javadoc-plugin - 2.10.3 attach-javadocs - jar + javadoc-no-fork + test-javadoc-no-fork @@ -452,7 +458,7 @@ repo.jenkins-ci.org - http://repo.jenkins-ci.org/public/ + https://repo.jenkins-ci.org/public/ diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 00000000..387c4133 --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,32 @@ + + + + + + org.apache.maven.skins + maven-fluido-skin + 1.5 + + + + + true + true + + ${project.url} + + + jenkinsci/java-client-api + right + gray + + + + + + + + +