This page explains how to expose Json objects over HTTP API in your Jenkins plugins, using GET and POST verbs.
This page also shows how to test it with JenkinsRules from jenkins-test-harness.
Pre-requisite: a POJO (Plain Old Java Object)
This object represents the structured data that is exchanged between the HTTP server (Jenkins) and the client (curl for example).
We are using a very simple java Object for example purpose, but in production code you will have more complex objects to manipulate.
Note that if you use Stapler (JSONObject) for marshalling and unmarshalling JSON, you need an empty constructor.
publicstaticclassMyJsonObject{privateStringmessage;//empty constructor required for JSON parsing.publicMyJsonObject(){}publicMyJsonObject(Stringmessage){this.message=message;}publicvoidsetMessage(Stringmessage){this.message=message;}publicStringgetMessage(){returnmessage;}}
java
Handling GET with Jenkins
This part shows how to expose an HTTP GET that return a structured JSON response.
Must be an extension to be discovered as a service by Jenkins.
This class is an extension of RootAction. It is a way to expose HTTP paths that is more frequently used to expose the Web UI, but it can also be used without HTML rendering.
Since there is no HTML/Jelly rendering, no icons are needed.
Since there is no HTML/Jelly rendering, no display name is needed.
getUrlName() is the root of the JSON API. Each WebMethod in the class is prefixed by this.
@GET indicates the HTTP method that is expected, and @WebMethod indicates that it is accepting an HTTP request. By default, the JAVA name of the WebMethod is used, but a different name can be specified by using name =
JsonHttpResponse is a subclass of HttpResponse that indicates that the response content will be JSON
An HTTP status can be set in the response.
@QueryParameter indicates that this parameter is injected from HTTP query parameter. By default, the JAVA parameter name is used (in this case paramValue), but a different name can be specified by using value = annotation attribute.
The method getError500 is added in the example to show how to set an error response as JSON.
Testing with CURL
A mvn hpi:run in the plugin should be enough to run it locally. Assuming that Jenkins is up at http://localhost:8080/jenkins/ , you should have at this point:
curl -XGET\-w"\n STATUS:%{http_code}"\
http://localhost:8080/jenkins/custom-api/get-example-param?paramValue=hello
{"message":"I am Jenkins hello"}
STATUS:200
bash
Example of test with JenkinsRule
importstaticorg.hamcrest.MatcherAssert.assertThat;importjenkins.model.Jenkins;importorg.hamcrest.Matchers;importorg.junit.jupiter.api.Test;importorg.jvnet.hudson.test.JenkinsRule;importorg.jvnet.hudson.test.JenkinsRule.JSONWebResponse;importorg.jvnet.hudson.test.MockAuthorizationStrategy;importorg.jvnet.hudson.test.junit.jupiter.WithJenkins;@WithJenkinsclassJsonAPITest{privatestaticfinalStringGET_API_URL="custom-api/get-example-param?paramValue=hello";@TestvoidtestGetJSON(JenkinsRulej)throwsException{JenkinsRule.WebClientwebClient=j.createWebClient();JSONWebResponseresponse=webClient.getJSON(GET_API_URL);assertThat(response.getContentAsString(),Matchers.containsString("I am Jenkins hello"));assertThat(response.getStatusCode(),Matchers.equalTo(200));}@TestvoidtestAdvancedGetJSON(JenkinsRulej)throwsException{//Given a Jenkins setup with a user "admin"MockAuthorizationStrategyauth=newMockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin");j.jenkins.setSecurityRealm(j.createDummySecurityRealm());j.jenkins.setAuthorizationStrategy(auth);//We need to setup the WebClient, we use it to call the HTTP APIJenkinsRule.WebClientwebClient=j.createWebClient();//By default if the status code is not ok, WebClient throw an exception//Since we want to assert the error status code, we need to set to false.webClient.setThrowExceptionOnFailingStatusCode(false);// - simple call without authentication should be forbiddenJSONWebResponseresponse=webClient.getJSON(GET_API_URL);assertThat(response.getStatusCode(),Matchers.equalTo(403));// - same call but authenticated using withBasicApiToken() should be fineresponse=webClient.withBasicApiToken("admin").getJSON(GET_API_URL);assertThat(response.getStatusCode(),Matchers.equalTo(200));}}
java
Handling POST with Jenkins
This section shows how to expose an HTTP endpoint that takes a structured JSON Object as input, and does a response with a JSON structured Object.
For this example the same Object is used as input and output, but you can also use different JSON structure for the response.
Starting from the class JsonAPI provided for GET example, add:
@POST@WebMethod(name="create")publicJsonHttpResponsecreate(@JsonBodyMyJsonObjectbody){//Do any logic required for creation//For the example purpose we just uppercase the message parsed from the request.JSONObjectresponse=newJSONObject();response.put("message",body.message.toUpperCase());returnnewJsonHttpResponse(response,200);}
java
Testing with CURL
A mvn hpi:run in the plugin should be enough to run it locally.
Assuming that Jenkins is up at http://localhost:8080/jenkins/ , you should have at this point:
Write a file my.json containing the JSON body:
{"message":"A nice message to send"}
json
Then, if you need a user and a token:
go on Jenkins UI
login as a user, for example 'myuser'
on the top right click on user name
go on configure (for this user)
in the section "API Token" create a new token.
For additional documentation on the token, please visit:
Starting from the class JsonAPITest provided for the GET example, add:
@TestvoidtestPostJSON(JenkinsRulej)throwsException{//Given a Jenkins setup with a user "admin"MockAuthorizationStrategyauth=newMockAuthorizationStrategy().grant(Jenkins.ADMINISTER).everywhere().to("admin");j.jenkins.setSecurityRealm(j.createDummySecurityRealm());j.jenkins.setAuthorizationStrategy(auth);//We need to setup the WebClient, we use it to call the HTTP APIJenkinsRule.WebClientwebClient=j.createWebClient();// Testing an authenticated POST that should answer 200 OK and return same jsonMyJsonObjectobjectToSend=newMyJsonObject("Jenkins is the way !");JenkinsRule.JSONWebResponseresponse=webClient.withBasicApiToken("admin").postJSON("custom-api/create",JSONObject.fromObject(objectToSend));//because API is returning the same object, we assert the input message.assertThat(response.getContentAsString(),Matchers.containsString("JENKINS IS THE WAY !"));assertThat(response.getStatusCode(),Matchers.equalTo(200));}
java
Some additional information
For people that are familiar with REST/JSON concept you may want to use other HTTP verbs. It should work, but since generally in Jenkins only GET and POST are used, this page only shows example for this 2 verbs.
You may also want to use several HTTP status code, following HTTP convention like 201 for created. It will also work, and the examples above are returning explicit 200 status to show how to manage the HTTP status that is return.
Some statuses are managed by Jenkins Core and may be returned automatically, like 403 when the user in the request does not have the required permission or is anonymous, or 404 when the HTTP API is not found.