Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Your Implementation must extend https://flexagon.com/function-sdk/7.0/flexagon/fd/model/integration/its/api/IssueTrackingSystem.html in order to be used by FlexDeploy. If you are not going to allow calling a method, you can leave the body blank, or throw an error in the body, but the method must be defined. 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.

...

  • 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
titleExample class RedmineServiceIntegration.java
Code Block

...

language

...

  • 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.

Example groovy RedmineIssueTrackingSystem.groovy
Code Block
languagegroovy
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) { 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);
java

...

package flexagon.fd.model.integration.its.impl.redmine;

import flexagon.fd.model.integration.its.api.IssueTrackingSystem;
import flexagon.fd.model.integration.its.api.IssueTrackingSystemInstanceProject;
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.WorkItemSearchInput;
import flexagon.fd.model.integration.its.api.WorkItemSearchResult;
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()
  {
    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 String getUserName()
  {
    return (String) getProperties().get(FDREDMINE_USER_NMAE);
  }

  private String getPassword()
  {
    return (String) getProperties().get(FDREDMINE_PASSWORD);
  }

  private FlexRESTClient getRestService()
    throws ApiException
  {
    return RestService.getService(getHostNameURL(), getUserName(), getPassword());
  }

  private FlexRESTClient getTicketWebResource(String pTicketNummber)
    throws ApiException
  {
    String ticketPath = getPropertyForTicket(FDREDMINE_TICKET_URL_PATTERN, pTicketNummber);
    return getRestService().path(ticketPath);
  }

  public String getWorkItemStatus(WorkItem ticket)
    throws ApiException
  {
    String methodName = "getTicketStatus";
    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());
    JsonObject issueObject = (JsonObject) jsonObject.get("issue");
    String statusName = null;
    if (issueObject != null)
    {
      javax.json.JsonObject status = issueObject.getJsonObject("status");
      if (status != null)
      {
        statusName = status.getString("name");
      }

    }
    LOG.logFinestExiting(methodName, statusName);
    return statusName;
  }

  /*
   * Populates ticket's data (e.g. Description with summary)
   */
  @Override
  public void populateWorkItem(WorkItem ticket)
    throws ApiException
  {
    String methodName = "populateWorkItem";
    LOG.logFinestEntering(methodName, ticket);

    FlexRESTClientResponse response;
    try
    {
      response = RestService.checkResponse(getTicketWebResource(ticket.getNumber()).get());
    }
    catch (FlexCheckedException e)
    {
      throw new ApiException(e);
    }

    JsonObject jsonObject = RestService.readJsonObject(response.getResponseString());
    if (jsonObject != null)
    {
      javax.json.JsonObject issue = jsonObject.getJsonObject("issue");
      if (issue != null)
      {
        ticket.setDescription(issue.getString("description"));
        javax.json.JsonObject tracker = issue.getJsonObject("tracker");
        if (tracker != null)
        {
          ticket.setType(tracker.getString("name"));
        }
      }
    }
    LOG.logFinestExiting(methodName);
  }


  /*
   * Builds absolute ticket's URL
   */
  @Override
  public String getWorkItemURL(WorkItem ticket)
    throws ApiException
  {
    String methodName = "getWorkItemURL";
    LOG.logFinestEntering(methodName, ticket);

    String url = getURLProperty(FDREDMINE_TICKET_URL_PATTERN, ticket.getNumber());
    url = url.replace(".json", "");
    LOG.logFinestExiting(methodName, url);
    return url;
  }

  /*
   * Adds a comment to a ticket
   *
   */
  @Override
  public void addCommentToWorkItem(WorkItem ticket, String comment)
    throws ApiException
  {
    String methodName = "addCommentToWorkItem";
    LOG.logFinestEntering(methodName, ticket.getNumber(), comment);
    JsonObject commentJson = Json.createObjectBuilder().add("issue", Json.createObjectBuilder().add("notes", comment)).build();

    FlexRESTClientResponse response;
    try
    {
      response = RestService.checkResponse(getTicketWebResource(ticket.getNumber()).mediatype(MediaType.APPLICATION_JSON_TYPE).put(commentJson.toString()));
    }
    catch (FlexCheckedException e)
    {
      throw new ApiException(e);
    }

    LOG.logFinestExiting(methodName);
  }

  @SuppressWarnings("oracle.jdeveloper.java.insufficient-catch-block")
  private int getStatusIdFromName(String pStatus)
    throws ApiException
  {
    String methodName = "getStatusIdFromName";
    LOG.logFinestEntering(methodName, pStatus);

    int status_id = -1;
    try
    {
      String statusNumber = pStatus.split("##")[0];
      // If user saves 10 or 10##In Progress, let's try to parse it as number
      status_id = Integer.parseInt(statusNumber);
    }
    catch (Exception nfe)
    {
      // not integer value, call API
    }

    if (status_id == -1)
    {
      // call API to do conversion
      try
      {
        FlexRESTClientResponse response = 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 (ApiException apie)
      {
        throw apie;
      }
      catch (FlexCheckedException e)
      {
        throw new ApiException(e);
      }
    }

    LOG.logFinestExiting(methodName, status_id);
    return status_id;
  }

  private Map<String, Integer> parseIssueStatuses(String pJson)
  {
    String methodName = "parseIssueStatuses";
    LOG.logFinestEntering(methodName, pJson);

    // Example output
    //    {
    //        "issue_statuses": [
    //            {
    //                "id": 1,
    //                "name": "New",
    //                "is_closed": false
    //            },
    //            {
    //                "id": 2,
    //                "name": "In Progress",
    //                "is_closed": false
    //            },
    //            {
    //                "id": 3,
    //                "name": "Resolved",
    //                "is_closed": false
    //            },
    //            {
    //                "id": 4,
    //                "name": "Closed",
    //                "is_closed": true
    //            },
    //            {
    //                "id": 5,
    //                "name": "Rejected",
    //                "is_closed": true
    //            },
    //            {
    //                "id": 6,
    //                "name": "Ready",
    //                "is_closed": false
    //            }
    //        ]
    //    }

    Map<String, Integer> map = new HashMap<>();

    JsonObject jsonObject = RestService.readJsonObject(pJson);
    JsonArray ar = jsonObject.getJsonArray("issue_statuses");
    for (int i = 0; i < ar.size(); i++)
    {
      String name = ar.getJsonObject(i).getString("name");
      int id = ar.getJsonObject(i).getInt("id");
      map.put(name, id);
    }

    LOG.logFinestExiting(methodName, map);
    return map;
  }

  /*
   * Changes ticket's status
   *
   */
  @Override
  public void changeWorkItemStatusTo(WorkItem ticket, Serializable status)
    throws ApiException
  {
    String methodName = "changeWorkItemStatusTo";
    LOG.logFinestEntering(methodName, ticket.getNumber(), status);

    // Redmine API needs id and not status name
    JsonObjectBuilder addStatus = Json.createObjectBuilder().add("notes", "Status updated to " + status);
    addStatus = addStatus.add("status_id", getStatusIdFromName((String) status));
    JsonObject statusJson = Json.createObjectBuilder().add("issue", addStatus).build();

    FlexRESTClientResponse response;
    try
    {
      response = RestService.checkResponse(getTicketWebResource(ticket.getNumber()).mediatype(MediaType.APPLICATION_JSON_TYPE).put(statusJson.toString()));
    }
    catch (FlexCheckedException e)
    {
      throw new ApiException(e);
    }

    LOG.logFinestExiting(methodName);
  }

  @Override
  public void checkConnection()
    throws ApiException
  {
    String methodName = "checkConnection";
    LOG.logFineEntering(methodName);

    try
    {
      String aboutUrl = "/users/current.json";
      LOG.logInfo(methodName, "Checking Redmine connection for user {0} with path {1}", getUserName(), getHostNameURL() + aboutUrl);
      FlexRESTClientResponse clientResponse = getRestService().path(aboutUrl).get();
      LOG.logInfo(methodName, "Successfully invoked test connection URL {0}", clientResponse);
      int statusCode = clientResponse.getResponse().getStatusInfo().getStatusCode();
      if (statusCode == 401)
      {
        throw new ApiException("Invalid credentials.", "");
      }
      RestService.checkResponse(clientResponse);
      LOG.logInfo(methodName, "Test connection response code looks valid, check content of response");
      RestService.readJsonObject(clientResponse.getResponseString());
      LOG.logInfo(methodName, "Validated that JSON data was received from test connction URL invocation.");
    }
    catch (ApiException e)
    {
      LOG.logInfo(methodName, "ApiException in Test connection", e);
      throw e;
    }
    catch (Exception e)
    {
      LOG.logInfo(methodName, "Failed in Test connection", e);
      throw new ApiException("Connection failed. " + e.getMessage(), "");
    }
    LOG.logFineExiting(methodName);
  }

  @Override
  public Collection<String> parseWorkItemNumberFromChangeLogs(List<String> pChangeLogMessages, List<String> pTicketPatternList)
    throws ApiException
  {
    String methodName = "parseWorkItemNumberFromChangeLogs";
    LOG.logFinestEntering(methodName, pChangeLogMessages, pTicketPatternList);
    
    Collection<String> ticketNumberList = new ArrayList<String>();
    Collection<String> parsedTicketNumbers = flexagon.fd.model.integration.its.util.ChangeLogParser.getParser().parse(pChangeLogMessages, pTicketPatternList);
    if (FlexCommonUtils.isNotEmpty(parsedTicketNumbers) && FlexCommonUtils.isNotEmpty(pTicketPatternList))
    {
      for (String parsedTicketWithPattern: parsedTicketNumbers)
      {
        for (String pattern: pTicketPatternList)
        {
          if (parsedTicketWithPattern.startsWith(pattern))
          {
            String ticketNumber = parsedTicketWithPattern.substring(pattern.length(), parsedTicketWithPattern.length());
            ticketNumberList.add(ticketNumber);
          }
        }
      }
    }
    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
  {
    // TODO Implement this method
    return Collections.emptyList();
  }

  @Override
  public List<WorkItemStatus> getAvailableWorkItemStatuses(WorkItem pWorkItem)
    throws ApiException
  {
    // TODO Implement this method
    return Collections.emptyList();
  }
  
  @Override
  public List<WorkItemSearchResult> searchWorkItems(WorkItemSearchInput pInput)
    throws ApiException
  {
    // TODO Implement this method (optional)
    return Collections.emptyList();
  }

  @Override
  public List<IssueTrackingSystemInstanceProject> getProjects()
    throws ApiException
  {
    // TODO Implement this method (optional)
    return Collections.emptyList();
  }
  
  @Override
  public boolean isExternalSearchSupported()
    throws ApiException
  {
    // TODO Implement this method (optional)   
    return false;
  }
  
  @Override
  public boolean isExternalSearchExcludeKeysSupported()
    throws ApiException
  {
    // TODO Implement this method (optional)   
    return false;
  }
  
  @Override
  public boolean isExternalSearchProjectRequired()
    throws ApiException
  {
    // TODO Implement this method (optional)   
    return false;
  }
  
  @Override
  public boolean isExternalSearchProjectSupported()
    throws ApiException
  {
    // TODO Implement this method (optional)  
    return false;
  }
  
  @Override
  public boolean isExternalSearchFilterRequired()
    throws ApiException
  {
    // TODO Implement this method (optional)   
    return false;
  }
}
  • 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/7.0/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.

Groovy Example
Expand
titleExample groovy RedmineIssueTrackingSystem.groovy
Code Block
languagegroovy
import flexagon.fd.model.integration.its.api.IssueTrackingSystemInstanceProject;
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.WorkItemSearchInput;
import flexagon.fd.model.integration.its.api.WorkItemSearchResult;
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
{
  void 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 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 = "getWorkItemURL()";
    String message = "ticket# ${ticket.toString()}"
    log.logInfo(methodName, message);
    String resourcePath = REDMINE_TICKET_URL_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
	message = "Redmine ticket URL is # ${REDMINE_URL}, resourcePath=${resourcePath}"
    log.logInfo(methodName, message)
	return REDMINE_URL + resourcePath;
  }
  
  void populateWorkItem(WorkItem ticket)
  {
    String methodName = "populateWorkItem()";
	String message = "Populate 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)
      {
        ticket.setDescription(issue.getString("description"))
        javax.json.JsonObject tracker = issue.getJsonObject("tracker")
        if(tracker !=null)
        {
          ticket.setType(tracker.getString("name"))
        }       
      }
    }
  }
  
  void addCommentToWorkItem(WorkItem ticket, String pComment)
  {
	String methodName = "addCommentToWorkItem()";   
    try
    { 
	  String message = " Adding comment to ${ticket.toString()} , comment=${pComment}"
      log.logInfo(methodName, message)
      def builder = new groovy.json.JsonBuilder()
      def root = builder.issue {
              notes "${pComment}"
       }
      String payload = builder.toString();
      String resourcePath = REDMINE_TICKET_
URL
REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}", ticket.getNumber())
    
javax.json.JsonObject
 
jsonObj
 
=
fdrestutils.
getRequest
putRequest(REDMINE_URL
, resourcePath
, REDMINE_USER_NMAE, REDMINE_PASSWORD
);
, 
if(jsonObj != null)
resourcePath, payload);
    
{
}
    
javax.json.JsonObject issue = jsonObj.getJsonObject("issue") if(issue!=null) {
catch (Exception e)
    {
   
javax.json.JsonObject
 
status
 
=
 
issue
log.
getJsonObject
logInfo(
"status")
 methodName, " Failed while adding comment to the 
if(status !=null) {
issue - " + e.getMessage() + " " + e)
      throw new ApiException("Connection failed. " 
return
+ 
status
e.
getString
getMessage(
"name"
));
    }
  }
}
  
  void changeWorkItemStatusTo(WorkItem ticket, Serializable pStatus)
  {
  
}
  String methodName 
}
= "changeWorkItemStatusTo()";
	
return null; }
String message = " Adding comment to ${ticket.toString()} , status=${pStatus}"
   
String
 
getWorkItemURL(WorkItem ticket
log.logInfo(methodName, message)
	def builder 
{
= new groovy.json.JsonBuilder()
String methodName
	def root = 
"getWorkItemURL()"; String message = "ticket# ${ticket.toString()}" log.logInfo(methodName, message);
builder.issue {
		notes "Status updated to ${pStatus}"
		status_id "${pStatus}"
	}
	String payload = builder.toString();
	String resourcePath = REDMINE_TICKET_
URL
REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}
", ticket.getNumber()) message = "Redmine ticket URL is # ${REDMINE_URL}, resourcePath=${resourcePath}" log.logInfo(methodName, message) return REDMINE_URL + resourcePath
", ticket.getNumber())
	fdrestutils.putRequest(REDMINE_URL, REDMINE_USER_NMAE, REDMINE_PASSWORD, resourcePath, payload);
  }
   
 
void
 def 
populateWorkItem(WorkItem ticket
parseWorkItemNumberFromChangeLogs(List<String> pChangeLogMessages, List<String> pTicketPatternList)
  {
	String methodName = 
String methodName
"parseWorkItemNumberFromChangeLogs()";
	Set<String> ticketNumberList = 
"populateWorkItem
new HashSet<String>()
"
;
	String
message = "Populate ticket# ${ticket.toString()
 message = " Input ChangeLogMessages=${pChangeLogMessages} , TicketPatternList=${pTicketPatternList}"
    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

    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())
   
javax.json.JsonObject
 
issue
{
=
 
jsonObj.getJsonObject("issue")
     
if(issue!=null)
parsedTicketNumbers.each{ parsedTicket ->
   
{
     pTicketPatternList.each{ pattern 
ticket.setDescription(issue.getString("description"
->
			if(parsedTicket.startsWith(pattern))
			{
				String ticketNumber = parsedTicket.substring(pattern.length(),parsedTicket.length())
				ticketNumberList.add(ticketNumber)
			}
javax.json.JsonObject tracker = issue.getJsonObject("tracker")
		}
      }
    
if(tracker !=null)
}
	return ticketNumberList;
  }
  
  Collection<WorkItemComment> 
{
getWorkItemComments(WorkItem pTicket, int i, int i2)
    
ticket.setType(tracker.getString("name"))
throws ApiException
  {
    
}
return Collections.emptySet();
  }
  
  Collection<WorkItemUpdate> getWorkItemHistory(WorkItem pTicket, int i, 
}
int i2)
   
}
 throws ApiException
}
  {
   
void
 
addCommentToWorkItem(WorkItem ticket, String pComment) { String methodName = "addCommentToWorkItem()"
// TODO Implement this method
    return Collections.emptySet();
  }

  Collection<WorkItemAttachment> 
try
getWorkItemAttachments(WorkItem pTicket)
   
{
 throws 
ApiException
 
String
 
message
{
=
 
"
 
Adding
 
comment
 
to ${ticket.toString()} , comment=${pComment}"
// TODO Implement this method
    return 
log
Collections.
logInfo
emptySet(
methodName, message
);
  }

  
def
WorkItemDetails 
builder = new groovy.json.JsonBuilder(
getWorkItem(WorkItem pTicket)
    throws ApiException
def
 
root
 
=
{
builder.issue
 
{
   // TODO Implement this method
    return null;
 
notes
 
"${pComment
}
"


  InputStream getWorkItemAttachmentContent(WorkItem pTicket, Serializable 
}
pSerializable)
    throws ApiException
 
String
 
payload
{
=
 
builder.toString();
   // TODO Implement this 
String
method
resourcePath
 
=
 
REDMINE_TICKET_REST_PATTERN.replaceAll("\\{REDMINE_ISSUE\\}",
 
ticket.getNumber())
 return null;
  }

fdrestutils.putRequest(REDMINE_URL,
 
REDMINE_USER_NMAE, REDMINE_PASSWORD, resourcePath, payload);
 List<WorkItemDetails> getWorkItems(List<WorkItem> pList)
    
}
throws ApiException
  {
catch
 
(Exception
 
e)
  // TODO Implement 
{
this method
    return 
log
Collections.
logInfo
emptyList(
methodName, " Failed while adding comment to the issue - " + e.getMessage() + " " + e) throw new ApiException("Connection failed. " + e.getMessage()
);
  }

  List<WorkItemStatus> getAvailableWorkItemStatuses(WorkItem pWorkItem)
    throws ApiException
  {
    // TODO Implement this method
    return Collections.emptyList();
  }
 
}
 
 
}
 List<WorkItemSearchResult> searchWorkItems(WorkItemSearchInput pInput)
  
void
 
changeWorkItemStatusTo(WorkItem
 
ticket, Serializable pStatus)
throws ApiException
  {
    String methodName = "
changeWorkItemStatusTo()
searchWorkItems";
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
 LOG.fine("Entering " + methodName + " [" + pInput + "]");
    
    // TODO - implement (optional)

    LOG.fine("Exiting " + methodName);
    return Collections.emptyList();
  }

  List<IssueTrackingSystemInstanceProject> getProjects()
 
def
 
parseWorkItemNumberFromChangeLogs(List<String>
 
pChangeLogMessages,
 
List<String>
throws 
pTicketPatternList)
ApiException
  {
    
String methodName = "
parseWorkItemNumberFromChangeLogs()
getProjects";
Set<String>
  
ticketNumberList
 
=
 
HashSet<String>(
LOG.fine("Entering " + methodName);
String
  
message
 
=
 
"

Input
 
ChangeLogMessages=${pChangeLogMessages}
 
,
 
TicketPatternList=${pTicketPatternList}" log.logInfo(methodName, message)
 // TODO - implement (optional)

    LOG.fine("Exiting " + methodName);
 
Collection<String>
  
parsedTicketNumbers
 
= flexagon.fd.model.integration.its.util.ChangeLogParser.getParser().parse(pChangeLogMessages, pTicketPatternList
return Collections.emptyList();
message
 
=
 
"parsedTicketNumbers=${parsedTicketNumbers}"
}
  
  
log.logInfo(methodName, message) if(parsedTicketNumbers != null && !parsedTicketNumbers.isEmpty() && pTicketPatternList != null && !pTicketPatternList.isEmpty()) {
boolean isExternalSearchSupported()
    throws ApiException
  {
    String methodName = "isExternalSearchSupported";
    LOG.fine("Entering " + methodName);
	
    // TODO - implement (optional)    
	
parsedTicketNumbers.each{
 
parsedTicket
 
->
  LOG.fine("Exiting " + methodName);
   
pTicketPatternList.each{
 
pattern
return 
-> 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
false;
  }
  
  boolean isExternalSearchExcludeKeysSupported()
    throws ApiException
  {
    String methodName = "isExternalSearchExcludeKeysSupported";
    LOG.fine("Entering " + methodName);
	
    // TODO - implement (optional)    
	
    LOG.fine("Exiting " + methodName);
    return false;
  }
  
  boolean isExternalSearchProjectRequired()
    throws ApiException
  {
    String 
//
methodName 
TODO Implement this method
= "isExternalSearchProjectRequired";
    LOG.fine("Entering " 
return Collections.emptySet(
+ methodName);
	
  
}
  // TODO 
public
- 
Collection<WorkItemAttachment>
implement 
getWorkItemAttachments
(
WorkItem pTicket
optional)    
throws
	
ApiException
   
{
 LOG.fine("Exiting " + methodName);
//
 
TODO
 
Implement
 
this method
 
return Collections.emptySet()
return false;
  }
  
public
  
WorkItemDetails
boolean 
getWorkItem
isExternalSearchProjectSupported(
WorkItem pTicket
)
    throws ApiException
  {
    String 
//
methodName 
TODO Implement this method
= "isExternalSearchProjectSupported";
    LOG.fine("Entering " 
return
+ 
null
methodName);
	
 
}
   // TODO 
public
- 
InputStream
implement 
getWorkItemAttachmentContent(WorkItem pTicket, Serializable pSerializable
(optional)    
throws
	
ApiException
   
{
 LOG.fine("Exiting " 
// TODO Implement this method
+ methodName);
    return 
null
false;
  }
  
 
public
 
List<WorkItemDetails>
boolean 
getWorkItems
isExternalSearchFilterRequired(
List<WorkItem> pList
)
    throws ApiException
  {
    String 
//
methodName 
TODO Implement this method
= "isExternalSearchFilterRequired";
    LOG.fine("Entering " 
return Collections.emptyList(
+ methodName);
	
 
}
   // TODO 
public
- 
List<WorkItemStatus>
implement 
getAvailableWorkItemStatuses
(
WorkItem pWorkItem
optional)    
throws
	
ApiException
   
{
 LOG.fine("Exiting " + methodName);
//
 
TODO
 
Implement
 
this
 
method
return 
Collections.emptyList()
false;
  }
}
  • 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.", "");

...