...
Here we are editing a custom issue tracking system with properties. You can add existing properties using the drop down on the create button.
...
API Implementation
Your Implementation must include the following methods extend IssueTrackingSystem in order to be used by FlexDeploy. If you are not going to allow updating ticketscalling a method, you can leave the body blank, or throw an error in the body, but the method must be defined.
...
Method Name
...
Parameter(s)
...
Return Type
...
Description
...
populateTicket
...
Ticket pTicket
...
void
...
This method should call the issue Tracking System using the ticket number (pTicket.getNumber()) and set the Description and Type
...
getTicketURL
...
Ticket pTicket
...
String
...
This method should construct the URL to access the ticket, which is used to link from FlexDeploy. (Ex. http://<hostname>:<port>/redmine/issues/<issuenumber>.json)
...
addCommentToTicket
...
Ticket pTicket,
String pComment
...
void
...
Adding pComment to the ticket. The ticket number (pTicket.getNumber()) from Ticket object should be used to get the Ticket and add the comment.
...
changeTicketStatusTo
...
Ticket pTicket,
String pStatus
...
void
...
Change the status of the ticket to pStatus. The ticket number (pTicket.getNumber()) from Ticket object should be used to get the Ticket and change the status. The status passed into the function is the status id.
...
getTicketStatus
...
Ticket pTicket
...
String
...
Get the current status of the ticket. The ticket number (pTicket.getNumber()) from Ticket object should be used to get the Ticket status.
...
checkConnection
...
void
...
This should invoke any status or heath check URL of the issue tracking system to ensure the system is up and running and FlexDeploy can connect with the provided credentials
...
parseTicketNumberFromChangeLogs
...
String pProjectName,
List<String> pMessagesToParse,
List<String> pTicketPatternList
...
Collection<String>
...
pProjectName - name of the project for which the Issue tracking system is configured
pMessageToParse - list of string from the SCM commit logs
pTicketPatternList - pattern configured in the project or for the issues tracking system.
Add a custom rule by project to parse the message log string using single or multiple patterns. You can also use a different pattern list. Should return a unique list of ticket numbers
Java Implementation
Here are the high level steps for a Java implementation. You can use any IDE to prepare this implementation.
...
If you choose to make a Java implementation, your IDE will verify that you have implemented all the required methods. If you choose the Groovy implementation, then the FlexDeploy UI will validate that your class contains the necessary methods. Groovy implementations can be used even with 3rd party jars that you place in libext, and allow for easier iterations. The advantage of a Java implementation is a potentially tighter integration with source control.
Java Implementation
Here are the high level steps for a Java implementation. You can use any IDE to prepare this implementation.
Create java class that extends flexagon.fd.model.integration.its.api.IssueTrackingSystem. Example shown below has testConnection method implemented which uses properties map to retrieve the configuration values to connect to the issue tracking system.
All properties defined are available in Map returned by getProperties method.
Java Example
Expand |
---|
title | Example class RedmineServiceIntegration.java |
---|
|
Code Block |
---|
| package flexagon.fd.model.integration.its.impl.redmine;
import flexagon.fd.model.integration.its.api.IssueTrackingSystem;
import flexagon.fd.model.integration.its.api |
|
...
All properties defined are available in Map returned by getProperties method.
Example class RedmineServiceIntegration.java
Code Block |
---|
|
package.WorkItem;
import flexagon.fd.model.integration.its.api.WorkItemAttachment;
import flexagon.fd.model.integration.its.api.WorkItemComment;
import flexagon.fd.model.integration.its.api.WorkItemDetails;
import flexagon.fd.model.integration.its.api.WorkItemStatus;
import flexagon.fd.model.integration. | its.impl.redmine;
import flexagon.fd.model.integration.its.api.IssueTrackingSystem;
import flexagon.fd.model.integration.its.api.WorkItem;
import flexagon.fd.model.integration.its.api.WorkItemAttachment;
import flexagon.fd.model.integration.its.api.WorkItemComment;
import flexagon.fd.model.integration.its.api.WorkItemDetails;
import flexagon.fd.model.integration.its.api.WorkItemStatus;
import flexagon.fd.model.integration.its.api.WorkItemUpdate;
import flexagon.fd.model.integration.util.ApiException;
import flexagon.fd.model.integration.util.RestService;
import flexagon.ff.common.core.exceptions.FlexCheckedException;
import flexagon.ff.common.core.logging.FlexLogger;
import flexagon.ff.common.core.rest.FlexRESTClient;
import flexagon.ff.common.core.rest.FlexRESTClientResponse;
import flexagon.ff.common.core.utils.FlexCommonUtils;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.core.MediaType;
public class RedmineIssueTrackingSystem
extends IssueTrackingSystem
{
private static final String CLZ_NAM = RedmineIssueTrackingSystem.class.getName();
private static final FlexLogger LOG = FlexLogger.getLogger(CLZ_NAM);
private static final String FDREDMINE_URL = "FDREDMINE_URL";
private static final String FDREDMINE_TICKET_URL_PATTERN = "FDREDMINE_TICKET_URL_PATTERN";
private static final String FDREDMINE_ISSUE = "{REDMINE_ISSUE}";
private static final String FDREDMINE_USER_NMAE = "FDREDMINE_USER_NMAE";
private static final String FDREDMINE_PASSWORD = "FDREDMINE_PASSWORD";
public RedmineIssueTrackingSystem()
{
superits.api.WorkItemUpdate;
import flexagon.fd.model.integration.util.ApiException;
import flexagon.fd.model.integration.util.RestService;
import flexagon.ff.common.core.exceptions.FlexCheckedException;
import flexagon.ff.common.core.logging.FlexLogger;
import flexagon.ff.common.core.rest.FlexRESTClient;
import flexagon.ff.common.core.rest.FlexRESTClientResponse;
import flexagon.ff.common.core.utils.FlexCommonUtils;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.json.Json;
import javax.json.JsonArray;
import javax.json.JsonObject;
import javax.json.JsonObjectBuilder;
import javax.ws.rs.core.MediaType;
public class RedmineIssueTrackingSystem
extends IssueTrackingSystem
{
private static final String CLZ_NAM = RedmineIssueTrackingSystem.class.getName();
private static final FlexLogger LOG = FlexLogger.getLogger(CLZ_NAM);
private static final String FDREDMINE_URL = "FDREDMINE_URL";
private static final String FDREDMINE_TICKET_URL_PATTERN = "FDREDMINE_TICKET_URL_PATTERN";
private static final String FDREDMINE_ISSUE = "{REDMINE_ISSUE}";
private static final String FDREDMINE_USER_NMAE = "FDREDMINE_USER_NMAE";
private static final String FDREDMINE_PASSWORD = "FDREDMINE_PASSWORD";
public RedmineIssueTrackingSystem()
{
super();
}
private StringBuilder getURLBuilder()
{
StringBuilder urlBuilder = new StringBuilder((String) getProperties().get(FDREDMINE_URL));
return urlBuilder;
}
private String getPropertyForTicket(String propertyName, String pTicketNummber)
{
String origPropValue = (String) getProperties().get(propertyName);
if (origPropValue != null)
{
return origPropValue.replace(FDREDMINE_ISSUE, pTicketNummber);
}
else
{
return null;
}
}
private String getURLProperty(String propertyName, String pTicketNummber)
{
String methodName = "getURLProperty";
LOG.logFinestEntering(methodName);
StringBuilder ticketURLBuilder = getURLBuilder();
String urlProperty = ticketURLBuilder.append(getPropertyForTicket(propertyName, pTicketNummber)).toString();
LOG.logFinestExiting(methodName, urlProperty);
return urlProperty;
}
private String getHostNameURL()
{
return getURLBuilder().toString();
}
private | StringBuildergetURLBuilderStringBuilder urlBuilder = new StringBuilder(return (String) getProperties().get(FDREDMINE_USER_ | URL));
return urlBuilder
String getPropertyForTicket(String propertyName,pTicketNummberStringorigPropValue = (String) getProperties().get( | propertyNameFDREDMINE_PASSWORD);
}
private | ifFlexRESTClient getRestService( | origPropValue!=null){origPropValue.replace(FDREDMINE_ISSUE, pTicketNummber);RestService.getService(getHostNameURL(), getUserName(), getPassword());
}
private FlexRESTClient | }getTicketWebResource(String pTicketNummber)
| else throws ApiException
{
String ticketPath = getPropertyForTicket(FDREDMINE_TICKET_URL_PATTERN, pTicketNummber);
return | null getRestService().path(ticketPath);
}
| }}String getWorkItemStatus(WorkItem ticket)
| privateStringgetURLProperty(StringpropertyName,StringpTicketNummber) getURLPropertygetTicketStatus";
LOG.logFinestEntering(methodName, ticket.getNumber());
FlexRESTClientResponse response;
try
| StringBuilderticketURLBuilder=getURLBuilder();StringurlPropertyticketURLBuilderappend(getPropertyForTicket(propertyName, pTicketNummber)).toString();checkResponse(getTicketWebResource(ticket.getNumber()).get());
}
catch (FlexCheckedException e)
{
| LOG.logFinestExiting(methodName,urlProperty);return urlProperty;privateString getHostNameURL()= RestService.readJsonObject(response.getResponseString());
| { returngetURLBuildertoStringget("issue");
String statusName = | }privateStringgetUserName(issueObject != null)
{
javax.json.JsonObject | return(String) getProperties().get(FDREDMINE_USER_NMAE= issueObject.getJsonObject("status");
| }privateStringgetPassword({return(String) getProperties().get(FDREDMINE_PASSWORD);}privateFlexRESTClientgetRestService()status.getString("name");
}
| throwsApiException{returnRestServicegetServicegetHostNameURL()getUserName(), getPassword()}privateFlexRESTClient getTicketWebResource(String pTicketNummber)
throws ApiException{StringticketPath=getPropertyForTicket(FDREDMINE_TICKET_URL_PATTERN, pTicketNummber);
(e.g. Description with summary)
| return getRestService().path(ticketPath);}
StringgetWorkItemStatuspopulateWorkItem(WorkItem ticket)
throws ApiException
{
String methodName = " | getTicketStatuspopulateWorkItem";
LOG.logFinestEntering(methodName, ticket | .getNumber();
FlexRESTClientResponse response;
try
{
response = RestService.checkResponse(getTicketWebResource(ticket.getNumber()).get());
}
catch (FlexCheckedException e)
{
throw new ApiException(e);
}
JsonObject jsonObject = RestService.readJsonObject(response.getResponseString());
| JsonObjectissueObject (JsonObject) null)
{
javax.json.JsonObject issue = jsonObject. | get;
String statusName = null; issueObjectissue != null)
{
ticket.setDescription(issue.getString("description"));
javax.json.JsonObject | statusissueObjectstatusstatusstatusName=statusticket.setType(tracker.getString("name"));
}
}
}
LOG.logFinestExiting(methodName | , statusName return statusName;
}Populatesdata (e.g. Description with summary)voidpopulateWorkItemgetWorkItemURL(WorkItem ticket)
throws ApiException
{
String methodName = " | populateWorkItemgetWorkItemURL";
LOG.logFinestEntering(methodName, ticket);
| FlexRESTClientResponse responseString url = getURLProperty(FDREDMINE_TICKET_URL_PATTERN, ticket.getNumber());
url = url.replace(".json", "");
LOG.logFinestExiting(methodName, url);
| try{response= RestService.checkResponse(getTicketWebResource(ticket.getNumber()).get());
}Adds a comment to a ticket
*
| catch(FlexCheckedExceptione){ public void addCommentToWorkItem(WorkItem ticket, String comment)
| thrownewApiException(e);}JsonObjectjsonObjectRestService.readJsonObject(response.getResponseString())if (jsonObject != null)LOG.logFinestEntering(methodName, ticket.getNumber(), comment);
| { javax.json.JsonObject issue = jsonObject.getJsonObject("issue")Json.createObjectBuilder().add("issue", Json.createObjectBuilder().add("notes", comment)).build();
FlexRESTClientResponse | if(issue!=null) try
{
response = RestService.checkResponse(getTicketWebResource(ticket | .setDescription(issue.getString("description".getNumber()).mediatype(MediaType.APPLICATION_JSON_TYPE).put(commentJson.toString()));
}
| javax.json.JsonObjecttracker= issue.getJsonObject("tracker");(FlexCheckedException e)
{
| if(tracker!= null){ LOG.logFinestExiting(methodName);
}
| ticket.setType(tracker.getString("name"));@SuppressWarnings("oracle.jdeveloper.java.insufficient-catch-block")
private int getStatusIdFromName(String pStatus)
| }} }String methodName = "getStatusIdFromName";
LOG. | logFinestExitinglogFinestEntering(methodName, pStatus);
| } /*
* Builds absolute ticket's URL
*/
@Override
public String getWorkItemURL(WorkItem ticket)
throws ApiException
{status_id = -1;
try
{
String | methodNamestatusNumber = pStatus.split(" | getWorkItemURL##")[0];
// If user saves | LOG.logFinestEntering(methodName, ticket);
String url = getURLProperty(FDREDMINE_TICKET_URL_PATTERN, ticket.getNumber());10 or 10##In Progress, let's try to parse it as number
| urlurl.replace(".json", ""Integer.parseInt(statusNumber);
| LOG.logFinestExiting(methodName, url);returnurl;} **Addsacommenttoaticket* */
@OverridepublicvoidaddCommentToWorkItem(WorkItemticket,Stringcomment)throwsApiException{StringmethodName="addCommentToWorkItem";LOG.logFinestEntering(methodName, ticket.getNumber(), comment);JsonObjectcommentJsonJsoncreateObjectBuildercheckResponse(getRestService(). | add", Json.createObjectBuilder().add("notes", comment)).build();_statuses.json").get());
Map<String, Integer> statusMap = parseIssueStatuses(response.getResponseString());
| FlexRESTClientResponseresponse;
if (statusMap.containsKey(pStatus))
| tryresponse= RestService.checkResponse(getTicketWebResource(ticket.getNumber()).mediatype(MediaType.APPLICATION_JSON_TYPE).put(commentJson.toString())); status_id = statusMap.get(pStatus);
}
}
catch ( | FlexCheckedExceptionenewApiException(e);LOG.logFinestExiting(methodName);
}(FlexCheckedException e)
{
| @SuppressWarnings("oracle.jdeveloper.java.insufficient-catch-block")privateintgetStatusIdFromNameString pStatusthrowsApiException{StringmethodName="getStatusIdFromName"; logFinestEnteringlogFinestExiting(methodName, | pStatusint =-1;tryprivate Map<String, Integer> parseIssueStatuses(String pJson)
| {statusNumberpStatus.split("##")[0];
"parseIssueStatuses";
LOG.logFinestEntering(methodName, pJson);
// Example output
| Ifusersaves10or10##InProgress,let'strytoparseitasnumberstatus_id = Integer.parseInt(statusNumber); }catch(Exceptionnfe) notintegervalue,callAPI}if(status_id == -1){//callAPItodoconversiontry{FlexRESTClientResponseresponse=RestService.checkResponse(getRestService().path("/issue_statuses.json").get());Map<String Integer> statusMap = parseIssueStatuses(response.getResponseString());if (statusMap.containsKey(pStatus)){status_id = statusMap.get(pStatus);}}catch(ApiExceptionapie){throwapie;}catch(FlexCheckedExceptione){thrownewApiException(e);}} LOG.logFinestExiting(methodName, status_id);returnstatus_id;}privateMap<String,Integer>parseIssueStatuses(StringpJson) {StringmethodName="parseIssueStatuses";LOG.logFinestEntering(methodName,pJson);// Example output{//"issue_statuses": [
1Newfalse2In ProgressRejected",
// "is_closed": | false3ResolvedReady",
// "is_closed": false
// } | ,
// ]
// }
Map<String, Integer> map = new HashMap<>();
| { //RestService.readJsonObject(pJson);
JsonArray ar = jsonObject.getJsonArray("issue_statuses");
for (int i | "id": 4,
//= 0; i < ar.size(); i++)
{
String name | = ar.getJsonObject(i).getString("name" | : "Closed",// int id = ar.getJsonObject(i).getInt("id");
map.put(name, id);
| "is_closed":true// LOG.logFinestExiting(methodName, map);
return map;
} | , /*
* Changes ticket's status
*
| { /*/
@Override
public void changeWorkItemStatusTo(WorkItem ticket, Serializable status)
| "id": 5,// String methodName = "changeWorkItemStatusTo";
LOG.logFinestEntering(methodName, | "name": "Rejected",ticket.getNumber(), status);
// Redmine API needs id and not status name
JsonObjectBuilder addStatus = | "is_closed": true
//Json.createObjectBuilder().add("notes", "Status updated to " + status);
addStatus = addStatus.add("status_id", getStatusIdFromName((String) status));
| }, // = Json.createObjectBuilder().add("issue", addStatus).build();
FlexRESTClientResponse response;
| {// = RestService.checkResponse(getTicketWebResource(ticket.getNumber()).mediatype(MediaType.APPLICATION_JSON_TYPE).put(statusJson.toString()));
| "id": 6,//(FlexCheckedException e)
{
throw new | "name": "Ready",// LOG.logFinestExiting(methodName);
}
@Override
public | "is_closed": falsevoid checkConnection()
throws | //ApiException
{
String methodName = "checkConnection";
| } //LOG.logFineEntering(methodName);
| ]
//}Map<String,Integer>mapnew HashMap<>()JsonObject jsonObject = RestService.readJsonObject(pJson);
JsonArray ar = jsonObject.getJsonArray("issue_statuses" LOG.logInfo(methodName, "Checking Redmine connection for user {0} with path {1}", getUserName(), getHostNameURL() + aboutUrl);
| for(inti0; i < ar.sizegetRestService().path(aboutUrl).get();
| i++){ LOG.logInfo(methodName, "Successfully invoked test connection URL | String name = ar.getJsonObject(i).getString("name"{0}", clientResponse);
int | idar.getJsonObject(iclientResponse.getResponse().getStatusInfo(). | getInt"id"map.put(name, id);
} LOG.logFinestExiting(methodName, map);
returnmap;} /*
* Changes ticket's status
*new ApiException("Invalid credentials.", "");
}
| */
RestService.checkResponse(clientResponse);
| @OverridepublicvoidchangeWorkItemStatusTo(WorkItem ticketSerializablestatus)connection response code looks valid, | throwsApiException{String methodName = "changeWorkItemStatusTo"; RestService.readJsonObject(clientResponse.getResponseString());
LOG. | logFinestEnteringticket.getNumber(), status);
// Redmine API needs id and not status name
JsonObjectBuilder addStatus = Json.createObjectBuilder().add("notes", "Status updated to " + status);"Validated that JSON data was received from test connction URL invocation.");
}
catch (ApiException e)
{
| addStatus= addStatusadd("status_id", getStatusIdFromName((String) status));
JsonObject statusJson = Json.createObjectBuilder().add("issue", addStatus).build();logInfo(methodName, "ApiException in Test connection", e);
| FlexRESTClientResponseresponsetry{response=RestService.checkResponse(getTicketWebResource(ticket.getNumber()).mediatype(MediaType.APPLICATION_JSON_TYPE).put(statusJson.toString()));}
catch (FlexCheckedExceptionLOG.logInfo(methodName, "Failed in Test connection", e);
| { throw new ApiException("Connection failed. " | throw new ApiException(e logFinestExitinglogFineExiting(methodName);
}
@Override
public | voidcheckConnection(parseWorkItemNumberFromChangeLogs(List<String> pChangeLogMessages, List<String> pTicketPatternList)
throws ApiException
{
String methodName = " | checkConnectionparseWorkItemNumberFromChangeLogs";
LOG. | logFineEnteringlogFinestEntering(methodName, pChangeLogMessages, pTicketPatternList);
Collection<String> ticketNumberList = new ArrayList<String>();
| try Collection<String> parsedTicketNumbers | {
= flexagon.fd.model.integration.its.util.ChangeLogParser.getParser().parse(pChangeLogMessages, pTicketPatternList);
| String aboutUrl = "/users/current.json";if (FlexCommonUtils.isNotEmpty(parsedTicketNumbers) && FlexCommonUtils.isNotEmpty(pTicketPatternList))
{
| LOG.logInfo(methodName,"CheckingRedmineconnectionuser {0} with path {1}", getUserName(), getHostNameURL() + aboutUrl);parsedTicketWithPattern: parsedTicketNumbers)
{
| FlexRESTClientResponseclientResponse=getRestService().path(aboutUrl).get();
(String pattern: pTicketPatternList)
| LOG.logInfo(methodName,"SuccessfullyinvokedtestconnectionURL0}", clientResponse);intstatusCode=clientResponse.getResponse)getStatusInfo.getStatusCode(;if(statusCode==401){throw new ApiException("Invalid credentials.", ""= parsedTicketWithPattern.substring(pattern.length(), parsedTicketWithPattern.length());
| } RestServicecheckResponseclientResponse
LOG.logInfo(methodName,"Testconnectionresponsecodelooksvalid,checkcontentofresponse");RestService.readJsonObject(clientResponse.getResponseString());
LOG.logInfo(methodName, "Validated that JSON data was received from test connction URL invocation." }
}
LOG.logFinestExiting(methodName);
| }return ticketNumberList;
}
| catch(ApiExceptione){Collection<WorkItemComment> getWorkItemComments(WorkItem pTicket, int i, int | LOG.logInfo(methodName, "ApiException in Test connection", e);i2)
throws ApiException
{
| throwe;// TODO Implement this method
| }catch (Exception e)Collections.emptySet();
}
| { LOG.logInfo(methodName, "Failed in Test connection", e);public Collection<WorkItemUpdate> getWorkItemHistory(WorkItem pTicket, int i, int i2)
throws ApiException
| thrownewApiException("Connection failed. " + e.getMessage(), "");
}{
// TODO Implement this method
return | LOGlogFineExitingmethodNameCollection<String>Collection<WorkItemAttachment> | parseWorkItemNumberFromChangeLogsList<String> pChangeLogMessages, List<String> pTicketPatternListWorkItem pTicket)
throws ApiException
{
| String methodName = "parseWorkItemNumberFromChangeLogs";
LOG.logFinestEntering(methodName, pChangeLogMessages, pTicketPatternList);
Collection<String> ticketNumberList = new ArrayList<String>// TODO Implement this method
return Collections.emptySet();
}
| Collection<String>parsedTicketNumbers= flexagon.fd.model.integration.its.util.ChangeLogParser.getParser().parse(pChangeLogMessages, pTicketPatternList);
if (FlexCommonUtils.isNotEmpty(parsedTicketNumbers) && FlexCommonUtils.isNotEmpty(pTicketPatternList))
@Override
public WorkItemDetails getWorkItem(WorkItem pTicket)
throws ApiException
{
// TODO | for(StringparsedTicketWithPattern:parsedTicketNumbers){forInputStream getWorkItemAttachmentContent( | String pattern: pTicketPatternListWorkItem pTicket, Serializable pSerializable)
throws ApiException
{
// TODO Implement this method
| if(parsedTicketWithPattern.startsWith(pattern)){ public List<WorkItemDetails> getWorkItems(List<WorkItem> pList)
throws ApiException
| StringticketNumber=parsedTicketWithPattern.substring(pattern.length(),parsedTicketWithPattern.length()); // TODO Implement this method
return | ticketNumberListaddticketNumber);
}
@Override
public List<WorkItemStatus> getAvailableWorkItemStatuses(WorkItem pWorkItem)
| }}}}
return Collections.emptyList();
| LOG.logFinestExiting(methodName);
return ticketNumberList;
}
@Override
public Collection<WorkItemComment> getWorkItemComments(WorkItem pTicket, int i, int i2)
throws ApiException
{
// TODO Implement this method
return Collections.emptySet();
}
@Override
public Collection<WorkItemUpdate> getWorkItemHistory(WorkItem pTicket, int i, int i2)
throws ApiException
{
// TODO Implement this method
return Collections.emptySet();
}
@Override
public Collection<WorkItemAttachment> getWorkItemAttachments(WorkItem pTicket)
throws ApiException
{
// TODO Implement this method
return Collections.emptySet();
}
@Override
public WorkItemDetails getWorkItem(WorkItem pTicket)
throws ApiException
{
// TODO Implement this method
return null;
}
@Override
public InputStream getWorkItemAttachmentContent(WorkItem pTicket, Serializable pSerializable)
throws ApiException
{
// TODO Implement this method
return null;
}
@Override
public List<WorkItemDetails> getWorkItems(List<WorkItem> pList)
throws ApiException |
In order to compile your java class, you will need FlexDeployAPI.jar on classpath.
Implement all the methods defined by https://flexagon.com/function-sdk/6.5/flexagon/fd/model/integration/its/api/IssueTrackingSystem.html.
For any failure connecting to the system or if any issues with the data, then you can throw exception. For example throw new ApiException("Invalid credentials.", "");
Once you are ready with unit testing, you can prepare Jar file for your credential store java class and other utility classes. This jar file can be placed on server classpath now.
Put this jar file in apache-tomcat-flexdeploy/libext folder.
If you are using any third party libraries from your Java implementation, then those jar files will also need to be added to same libext folder.
Groovy Implementation
Here are the high level steps for a Groovy implementation. You can use any IDE to prepare this implementation.
As groovy is able to access FlexDeploy variables and Java classes, you can take advantage of Java libraries from Groovy script. For example, if there is Java library used to access the issue tracking system, you can places those in lib folder and use those classes from Groovy script. This allows you to keep dynamic part of implementation in Groovy and use Java library.
Create a groovy class. The example shown below has testConnection and some other methods implemented.
You need to implement all methods defined in https://flexagon.com/function-sdk/6.5/flexagon/fd/model/integration/its/api/IssueTrackingSystem.html .
All properties defined are available as groovy binding variables. For example, properties can be accessed directly like REDMINE_URL, REDMINE_USER_NAME, REDMINE_PASSWORD, etc,
Groovy Example
Expand |
---|
title | Example groovy RedmineIssueTrackingSystem.groovy |
---|
|
Code Block |
---|
| import flexagon.fd.model.integration.its.api.WorkItem;
import flexagon.fd.model.integration.its.api.WorkItemAttachment;
import flexagon.fd.model.integration.its.api.WorkItemComment;
import flexagon.fd.model.integration.its.api.WorkItemDetails;
import flexagon.fd.model.integration.its.api.WorkItemStatus;
import flexagon.fd.model.integration.its.api.WorkItemUpdate;
import flexagon.fd.model.integration.util.ApiException;
class RedmineIssueTrackingSystem extends IssueTrackingSystem
{
void checkConnection()
{
String methodName = "checkConnection()";
try
{
fdrestutils.testConnection(REDMINE_URL, "/projects/1/versions.json", REDMINE_USER_NMAE, REDMINE_PASSWORD)
}
catch (Exception e)
{
| //TODO Implement this method
return Collections.emptyList();
}
@Override
public List<WorkItemStatus> getAvailableWorkItemStatuses(WorkItem pWorkItem)
throws ApiException
{
// TODO Implement this method
return Collections.emptyList();
}
}
|
In order to compile your java class, you will need FlexDeployAPI.jar on classpath.
Implement all the methods described in the table in the API Implementation section.
For any failure connecting to the system or if any issues with the data, then you can throw exception. For example throw new ApiException("Invalid credentials.", "");
Once you are ready with unit testing, you can prepare Jar file for your credential store java class and other utility classes. This jar file can be placed on server classpath now.
Put this jar file in apache-tomcat-flexdeploy/libext folder.
If you are using any third party libraries from your Java implementation, then those jar files will also need to be added to same libext folder.
Groovy Implementation
Here are the high level steps for a Groovy implementation. You can use any IDE to prepare this implementation.
As groovy is able to access FlexDeploy variables and Java classes, you can take advantage of Java libraries from Groovy script. For example, if there is Java library used to access the issue tracking system, you can places those in lib folder and use those classes from Groovy script. This allows you to keep dynamic part of implementation in Groovy and use Java library.
Create a groovy class. Example shown below has testConnection and other methods implemented.
All properties defined are available as groovy binding variables. For example, properties can be accessed directly like REDMINE_URL, REDMINE_USER_NAME, REDMINE_PASSWORD, etc,
Example groovy RedmineIssueTrackingSystem.groovy
Code Block |
---|
|
import flexagon.fd.model.integration.its.api.Ticket;
import flexagon.fd.model.integration.util.ApiException;
class RedmineIssueTrackingSystem
{
def checkConnection()
{
String methodName = "checkConnection()";
try
{
fdrestutils.testConnection(REDMINE_URL, "/projects/1/versions.json", REDMINE_USER_NMAE, REDMINE_PASSWORD)
}
catch (Exception e)
{
log.logInfo(methodName, " Failed in Test connection" + e.getMessage() + " " + e)
throw new ApiException("Connection failed. " + e.getMessage());
}
}
String getTicketStatus(Ticket log.logInfo(methodName, " Failed in Test connection" + e.getMessage() + " " + e)
throw new ApiException("Connection failed. " + e.getMessage());
}
}
String getWorkItemStatus(WorkItem ticket)
{
String methodName = "getWorkItemStatus()";
String message = " ticket# ${ticket.toString()}"
log.logInfo(methodName, message);
String resourcePath = REDMINE_TICKET_URL_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
javax.json.JsonObject jsonObj = fdrestutils.getRequest(REDMINE_URL, resourcePath, REDMINE_USER_NMAE, REDMINE_PASSWORD);
if(jsonObj != null)
{
javax.json.JsonObject issue = jsonObj.getJsonObject("issue")
if(issue!=null)
{
javax.json.JsonObject status = issue.getJsonObject("status")
if(status !=null)
{
return status.getString("name")
}
}
}
return null;
}
String getWorkItemURL(WorkItem ticket)
{
String methodName = " | getTicketStatusgetWorkItemURL()";
String message = " | ticket# ${ticket.toString()}"
log.logInfo(methodName, message);
String resourcePath = REDMINE_TICKET_URL_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
message = "Redmine ticket | javax.json.JsonObject jsonObj = fdrestutils.getRequest(URL is # ${REDMINE_URL}, resourcePath=${resourcePath}"
log.logInfo(methodName, | REDMINE_USER_NMAE,PASSWORD)if(jsonObj!=null) void populateWorkItem(WorkItem ticket)
| {javax.json.JsonObject issuejsonObj.getJsonObject"issue")
)";
String message = "Populate ticket# ${ticket.toString()}"
| if(issue!=null)log.logInfo(methodName, message)
String resourcePath | {
= REDMINE_TICKET_URL_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
| statusissuegetJsonObject("status")
getRequest(REDMINE_URL, resourcePath, REDMINE_USER_NMAE, REDMINE_PASSWORD);
if( | status
{
javax.json.JsonObject issue = | return status.getStringname }
}
}
return null;}StringgetTicketURL(Ticket ticket.setDescription(issue.getString("description"))
| {String methodNamejavax.json.JsonObject tracker = | "getTicketURL()";issue.getJsonObject("tracker")
| Stringmessage="ticket# ${ticket.toString()}"log.logInfo(methodName,message);StringresourcePath= REDMINE_TICKET_URL_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
message = "Redmine ticket URL is # ${REDMINE_URL}, resourcePath=${resourcePath}" ticket.setType(tracker.getString("name"))
}
| log.logInfo(methodName, message)
return REDMINE_URL + resourcePath;defpopulateTicketTicketWorkItem ticket, String pComment)
{
| populateTicketaddCommentToWorkItem()";
try
{
String message = " | ="Populateticket#to ${ticket.toString()} , comment=${pComment}"
log.logInfo(methodName, message)
| StringresourcePath= REDMINE_TICKET_URL_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
javax.json.JsonObject jsonObj = fdrestutils.getRequest(REDMINE_URL, resourcePath, REDMINE_USER_NMAE, REDMINE_PASSWORD);
if(jsonObj != null)def builder = new groovy.json.JsonBuilder()
def root = builder.issue {
| {javax.json.JsonObjectissue=jsonObj.getJsonObject("issue")if(issue!=null) String payload = builder.toString();
| { ticket.setDescription(issue.getString("description"))= REDMINE_TICKET_REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
fdrestutils.putRequest(REDMINE_URL, REDMINE_USER_NMAE, REDMINE_PASSWORD, | javax.json.JsonObject tracker = issue.getJsonObject("tracker")
resourcePath, payload);
}
| iftracker!=null{ log.logInfo( methodName, " Failed while adding comment | ticket.setType(tracker.getString("name"))
} to the issue - " + e.getMessage() + " " + e)
throw new ApiException("Connection failed. " | }defaddCommentToTicketTicketStringpComment addCommentToTicketchangeWorkItemStatusTo()";
|
try
{
String message = " Adding comment to ${ticket.toString()} , | commentpComment
log.logInfo(methodName, message)
| def builder = new groovy.json.JsonBuilder()
def root = builder.issue {
notes "Status updated to ${pStatus}"
status_id "${pStatus}"
}
String payload = | def rootbuilder.toString();
String resourcePath = | builder.issue {
REDMINE_TICKET_REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
fdrestutils.putRequest(REDMINE_URL, REDMINE_USER_NMAE, REDMINE_PASSWORD, resourcePath, payload);
}
| notes "${pComment}"
def parseWorkItemNumberFromChangeLogs(List<String> pChangeLogMessages, List<String> pTicketPatternList)
{
| } String methodName = "parseWorkItemNumberFromChangeLogs()";
| String payload Set<String> ticketNumberList = | builder.toStringHashSet<String>();
String message = " Input | String resourcePath = REDMINE_TICKET_REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())ChangeLogMessages=${pChangeLogMessages} , TicketPatternList=${pTicketPatternList}"
log.logInfo(methodName, message)
| fdrestutils.putRequest(REDMINE_URL, REDMINE_USER_NMAE, REDMINE_PASSWORD, resourcePath, payload);
}
catch (Exception e)
{
log.logInfo( methodName, " Failed while adding comment to the issue - " + e.getMessage() + " " + e)
throw new ApiException("Connection failed. " + e.getMessage());
}
}
def changeTicketStatusTo(Ticket ticket, String pStatus)
{
String methodName = "changeTicketStatusTo()";
String message = " Adding comment to ${ticket.toString()} , status=${pStatus}"
log.logInfo(methodName, message)
def builder = new groovy.json.JsonBuilder()
def root = builder.issue {
notes "Status updated to ${pStatus}"
status_id "${pStatus}"
}
String payload = builder.toString();
String resourcePath = REDMINE_TICKET_REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
fdrestutils.putRequest(REDMINE_URL, REDMINE_USER_NMAE, REDMINE_PASSWORD, resourcePath, payload);
}
def parseTicketNumberFromChangeLogs(String pProjectName, List<String> pChangeLogMessages, List<String> pTicketPatternList)
{
String methodName = "parseTicketNumberFromChangeLogs()";
Set<String> ticketNumberList = HashSet<String>();
String message = " Input ProjectName= ${pProjectName}, ChangeLogMessages=${pChangeLogMessages} , TicketPatternList=${pTicketPatternList}"
log.logInfo(methodName, message)
if("Gradle".equals(pProjectName)) {
String[] patterns = ["ZZZ-"];
pTicketPatternList = Arrays.asList(patterns);
message = "override pattern for Gradle TicketPatternList=${pTicketPatternList}"
log.logInfo(methodName, message)
Collection<String> parsedTicketNumbers = flexagon.fd.model.integration.its.util.ChangeLogParser.getParser().parse(pChangeLogMessages, pTicketPatternList);
message = "parsedTicketNumbers=${parsedTicketNumbers}"
log.logInfo(methodName, message)
if(parsedTicketNumbers != null && !parsedTicketNumbers.isEmpty() && pTicketPatternList != null && !pTicketPatternList.isEmpty())
{
parsedTicketNumbers.each{ parsedTicket ->
pTicketPatternList.each{ pattern ->
if(parsedTicket.startsWith(pattern))
{
String ticketNumber = parsedTicket.substring(pattern.length(),parsedTicket.length())
ticketNumberList.add(ticketNumber)
}
}
}
}
return ticketNumberList;
}
public Collection<WorkItemComment> getWorkItemComments(WorkItem pTicket, int i, int i2)
throws ApiException
{
return Collections.emptySet();
}
public Collection<WorkItemUpdate> getWorkItemHistory(WorkItem pTicket, int i, int i2)
throws ApiException
{
// TODO Implement this method
return Collections.emptySet();
}
public Collection<WorkItemAttachment> getWorkItemAttachments(WorkItem pTicket)
throws ApiException
{
// TODO Implement this method
return Collections.emptySet();
}
public WorkItemDetails getWorkItem(WorkItem pTicket)
throws ApiException
{
// TODO Implement this method
return null;
}
public | Collection<String>parsedTicketNumbers = flexagon.fd.model.integration.its.util.ChangeLogParser.getParser().parse(pChangeLogMessages, pTicketPatternList);
message = "parsedTicketNumbers=${parsedTicketNumbers}"getWorkItemAttachmentContent(WorkItem pTicket, Serializable pSerializable)
throws ApiException
{
| log.logInfo(methodName, message)
// TODO Implement this method
| if(parsedTicketNumbers!=&& !parsedTicketNumbers.isEmpty() && pTicketPatternList != null && !pTicketPatternList.isEmpty())
}
public List<WorkItemDetails> getWorkItems(List<WorkItem> pList)
throws ApiException
{
// TODO Implement this method
| parsedTicketNumbers.each{parsedTicket-> return Collections.emptyList();
}
| pTicketPatternList.each{pattern->
if(parsedTicket.startsWith(pattern))
{
String ticketNumber = parsedTicket.substring(pattern.length(),parsedTicket.length())
ticketNumberList.add(ticketNumber)
}
}
}
}
return ticketNumberListList<WorkItemStatus> getAvailableWorkItemStatuses(WorkItem pWorkItem)
throws ApiException
{
// TODO Implement this method
return Collections.emptyList();
} |
|
Implement all the methods described in the table in the API Implementation section
For any failure connecting to the system or if any issues with the data, then you can throw exception. For example, throw new ApiException("Invalid credentials.", "");
...