This guide will help you configure your Salesforce using the Connector Fetch REST API so that it has the ability to display Jira information via the Visualforce page. The information will be called through the Fetch Endpoint provided by the Connector.
This method will require writing several Apex Classes, the codes of which will be provided below. Also provided are Unit Test Codes for the Fetch REST API.
All Apex Class codes provided are also provided on our Bitbucket Repository for your reference.
This setup can only be used if your Jira instance is not behind a firewall.
Writing the Apex Class
Step 1: Write the APEX controller
The Fetch REST API can be called from within an Apex Class. This example will guide you on how to call the Fetch REST API.
Before you write Apex code that calls a remote URL, you have to allow Salesforce to make a call to the external server.
- Log in into Salesforce.
- Go to Your Name > Setup > Administration Setup > Security Controls > Remote Site Settings.
- Add new remote site.
- Enter the name of remote site.
- Enter your Jira URL in Remote Site URL, e.g.:
http://yourjiraurl.com/
- Click Save.
Once you are done, you can start writing the code. Here are the steps to write Salesforce Apex Class.
- Log in into Salesforce.
- Go to Setup > App Setup > Develop > Apex Classes > New. (If you have already created this Apex Class, you do not need to do this step).
Paste the Apex Class parent code into the field.
- Now, create another Apex Class (follow the same steps in Step 2) and paste the following code into the field:
public class JIRAFetchIssuesController { private String issuesJson; // Constructor of this class. Note that a request will be sent to JIRA when this class is constructed. public JIRAFetchIssuesController(ApexPages.StandardController stdController) { Case theCase = (Case)stdController.getRecord(); this.issuesJson = getResultJson('Case', theCase.id); } // Sends request to JIRA and returns the request body which should be a valid JSON. private static String getResultJson(String objectType, String objectId) { try { HttpRequest req = buildRequest(JIRA.baseUrl, JIRA.username, JIRA.password, JIRA.systemId, objectType, objectId); HttpResponse res = JIRA.sendRequest(req); return res.getBody(); } catch(System.CalloutException e) { System.debug(e); return ''; } } // Constructs request needed to fetch JIRA issues from provided parameters. @testVisible private static HttpRequest buildRequest(String baseUrl, String username, String password, String systemId, String objectType, String objectId) { HttpRequest req = new HttpRequest(); String basicAuthHeader = JIRA.authHeader(username, password); String endpoint = getEndpoint(baseUrl, systemId, objectType, objectId); req.setHeader('Authorization', basicAuthHeader); req.setHeader('Content-Type','application/json'); req.setMethod('GET'); req.setEndpoint(endpoint); return req; } // Creates the endpoint to fetch the issue from provided parameters. private static String getEndpoint(String baseUrl, String systemId, String objectType, String objectId) { return baseUrl + '/rest/customware/connector/1.0/' + systemId + '/' + objectType + '/' + objectId + '/issue/fetch.json'; } public String getIssuesJSON() { return this.issuesJson; } public List<JIRAIssue> getIssues() { return (List<JIRAIssue>)JSON.deserialize(this.issuesJson , List<JIRAIssue>.class); } // JIRA Issue Object. @testVisible class JIRAIssue { public String summary { get; } public String project { get; } public String reporter { get; } public String key { get;} public String status { get; } public String resolution { get; } public String url { get; } public String type { get; } public String assignee { get; } public String description { get; } public String priority { get; } public String due_date { get; } } }
Copying the above code and pasting it directly into the field may result in additional, unwanted characters being included. To avoid this from happening, we recommend pasting the code into a text editor that accepts plain text (e.g. Notepad.exe) and then recopying it again before pasting it into the field.
Since the example code above uses Case for the Salesforce Object, remember to change the code where it references "Case
" to the the Salesforce Object you are using.
For example, replace "Case
" according to your Object type in the following line of code:
getResultJson('Case', theCase.id)
would be changed to:
getResultJson('Account', theAccount.id)
Using the REST API to fetch JIRA issue date data will return the value in the epoch date format. You will need to manually convert the value to a more human readable format. View our KB article for more information.
Step 2: Create the Visualforce page
Now that the Apex controller has been written, we can use it in Visualforce page. Let's create a Visualforce page:
- Go to Setup > App Setup > Develop > Pages > New
- In "label" field: FetchJIRAIssues
- In "name" field: FetchJIRAIssues
- In "Visualforce Markup", paste the code below.
- Click Save.
<apex:page standardController="Case" extensions="JIRAFetchIssuesController" > <!-- Uncomment next line if you want to render the returned JSON object --> <!-- {!IssuesJSON}--> <apex:pageblock title="JIRA Issues" rendered="{!Issues.size > 0}"> <!-- Create table for JIRA issues. With --> <apex:pageBlockTable value="{!Issues}" var="issue"> <!-- Issue Key column --> <apex:column headerValue="Issue Key"> <!-- Make it link to JIRA Issue --> <apex:outputLink value="{!issue.url}" target="_blank">{!issue.key}</apex:outputLink> </apex:column> <!-- Issue Resolution column --> <apex:column headerValue="Resolution" value="{!issue.resolution}"/> <!-- Issue Status column --> <apex:column headerValue="Status" value="{!issue.status}" /> <!-- Issue Assignee column --> <apex:column headerValue="Assignee" value="{!issue.assignee}" /> </apex:pageBlockTable> </apex:pageblock> <apex:pageblock title="JIRA Issues" rendered="{!Issues.size == 0}"> <apex:outputLabel >No JIRA Issue associated with this Case</apex:outputLabel> </apex:pageblock> </apex:page>
Copying the above code and pasting it directly into the field may result in additional, unwanted characters being included. To avoid this from happening, we recommend pasting the code into a text editor that accepts plain text (e.g. Notepad.exe) and then recopying it again before pasting it into the field.
You can modify the code accordingly, based on what field do you want to show.
Step 3: Add the created Visualforce page to the Case layout
Finally, we need to use the Visualpage created in the layout. In our example, we will associate the page in Case Layout:
- Go to Setup > App Setup > Customize > Cases > Page Layout
- In any of the page layout, add New Section.
- Add the FetchJIRAIssues Visualforce page to that section.
- Click Save.
Step 4: Create a Jira Issue based on a Salesforce Case
To see the result, create a Jira Issue based on a Salesforce Case and you will see the page in the Case.
Set security permission for user access to the created page
For the created page to be accessible by certain user in your Salesforce organization, you will need to enable profiles to access the created class and page.
For the created Apex Class:
- Go to Setup > Develop > Apex Classes.
- In your JIRAFetchIssues class, click on Security.
- Enable the profile of users that you want to give access to.
For VisualForce page:
- Go to Setup > Develop > Pages.
- In your JIRAFetchIssues page, click on Security.
- Enable the profile of users that you want to give access to.
Now your created page will be accessible to permitted user profiles.
Unit test for Fetch REST API
Provided below is the Unit Test code for this class.
Do remember to also include the Fixtures and Mock Objects found on the Apex Class Parent Code page before using the following code:
@isTest private class JIRAFetchIssuesControllerTest { // Tests buildRequest method in JIRAFetchIssuesController. static testMethod void buildRequestTest() { HttpRequest req = JIRAFetchIssuesController.buildRequest(TestFixture.baseUrl, TestFixture.username, TestFixture.password, TestFixture.systemId, TestFixture.objectType, TestFixture.objectId); System.assertEquals(req.getMethod(), 'GET'); System.assertEquals(req.getEndpoint(), 'http://jira.com/rest/customware/connector/1.0/1/Case/1/issue/fetch.json'); } // Tests whether the response is fetched successfully when JIRAFetchIssuesController is constructed. static testMethod void getIssuesJSONTest() { Case c = new Case(); insert c; Test.startTest(); Test.setMock(HttpCalloutMock.class, new MockHttpResponseFetchJIRAIssue()); JIRAFetchIssuesController controller = new JIRAFetchIssuesController(new ApexPages.StandardController(c)); System.assertEquals(TestFixture.jiraIssueResponseBody, controller.getIssuesJSON()); List<JIRAFetchIssuesController.JIRAIssue> jiraIssues = controller.getIssues(); JIRAFetchIssuesController.JIRAIssue issue1 = jiraIssues[0]; JIRAFetchIssuesController.JIRAIssue issue2 = jiraIssues[1]; System.assertEquals('Project One', issue1.summary); System.assertEquals('project one', issue1.project); System.assertEquals('admin@gmail.com', issue1.reporter); System.assertEquals('PO-1', issue1.key); System.assertEquals('open', issue1.status); System.assertEquals('yes', issue1.resolution); System.assertEquals('www.google.com', issue1.url); System.assertEquals('bug', issue1.type); System.assertEquals('admin', issue1.assignee); System.assertEquals('issue 1', issue1.description); System.assertEquals('high', issue1.priority); System.assertEquals('12-12-2015', issue1.due_date); System.assertEquals('Project Two', issue2.summary); System.assertEquals('project two', issue2.project); Test.stopTest(); } }