Saturday, September 6, 2014

Atlassian Lego Jira plugin development: simple multi-functional plugin with services (Lego,RaspberryPi, Netty)

   Not so long time ago I've been doing  small presentation about how to create various plugins into  the Alassian ecosystem, mainly Jira and Confluence. I want to share some step how to deal with plugin that aggregates more functionalities (CRUD Servlet with Services, Project Template and REST api). 
One of the issue here is the version of the Atlassian libraries you will use for your development. Reason is obvious, compatibility and another repositories. So let's take a look on maven descriptor file pom.xml. 
...

    
      jfrog
      http://repo.jfrog.org/artifactory/libs-releases
    
    
      atlassian-public
      https://maven.atlassian.com/repository/public
      
        true
        never
        warn
      
      
        true
        warn
      
    
  
...

    5.0.4
    6.1.7
    2.5.0
    3.0.0-m24
    2.7.1
    2.18
    

    4.3.5
    2.1

    
    1.0-SNAPSHOT
    
    4.11
    1.8.5

...
  The nice thing on the plugin development is testing of all parts during the process. Atlassian provides some testing libraries (ex. jira-tests or jira-func-tests) in additions with others (harmcrest matcher, mockito, junit etc...) 
Img.1.: heart of the plugin -> descriptor file -> atlasssian-plugin.xml
The most important of the plugins is the heart (Img.1.) represented by descriptor file. 

  
    ${project.description}
    ${project.version}
    
    images/pluginIcon.png
    images/pluginLogo.png
  
  

  
    The LEGO CRUD Issue Plugin
    /issuecrud
  

  
  
  

  
  
    Lego CRUD Resources
    com.atlassian.auiplugin:ajs
    com.atlassian.auiplugin:jquery

    
      
    

    

    
    
    

    atl.general
  

  
    com.example.plugins.tutorial.servlet.LegoBrickService
  

  
  
    Provides Lego Brick services.
  

  
  
    

It shows which technologies (Soy ~ Google Closure, Blue-prints, Velocity ...) we use inside the plugin and which parts of jira core services we employ to make the plugin work according what we are expecting from it:
Img.2. - add1.: Lego Project Template is INSIDE like Intel
1. Lego project template (Img.2., Img.3.)
2. Lego Issue tracker 
3. Lego REST API 
Img.3. - add1.: Lego Template is there with Customised workflows and Issues
Add1.:: From the descriptor file we can read that for the template we do use Soy file. Closures allows us to dynamically generates the web page content
{namespace JIRA.Templates.ProjectLegoTemplates.Lego}
...
{template .renderLegoProjectTemplateExplanation}
    {getText('lego.project.template.info.page.description')}


    ...
    
  • {getText('lego.project.template.reason.1')}
  • {getText('lego.project.template.reason.2')}
  • {getText('lego.project.template.reason.3')}
... {/template} ...
and together with all messages having stored in property files makes internationalisation process latter much more easier. The whole project workflow together with issues is defined by *.json file
{
    "issue-type-scheme":
    {
        "name": "lego.project.template.issuetype.scheme.name",
        "description": "lego.project.template.issuetype.scheme.description",
        "issue-types": [
            {
                "key": "issueType1",
                "name": "New Lego Feature",
                "description": "A new Lego JSON feature of the product, which has yet to be developed.",
                "icon": "/images/icons/newfeature.png",
                "workflow": "wf1"
            },
            ...

    },
    "workflow-scheme":
    {
        "name": "lego.project.template.workflow.scheme.name",
        "description": "lego.project.template.workflow.scheme.description",
        "default-workflow": "wf1",
        "workflows": [
            {
                "key": "wf1",
                "name": "lego.project.template.workflow.wf1.name",
                "workflow-bundle": "/wfb/Issue-Tracking-Workflow.jwb"
            },
            ...
...
and the project is hooked with the JIRA system by using Blueprints API. Specifically implementing the interface AddProjectHook with @Overriden methods validate and configure .  
...
public class LegoProjectHook implements AddProjectHook {
    ...
    @Override
    public ValidateResponse validate(final ValidateData validateData) {
       ...
    }

    @Override
    public ConfigureResponse configure(final ConfigureData configureData) {
       ...
    }
...

  The result looks pretty cool because now we have inside the JIRA official Lego Project with its own issues and flows how to solve them. We can also track whole development. 
Img.4.: Lego Project inside the JIRA 
Add2.:: Lego issue tracker. 
   For those who has experiences with Lego MindStorm EV3 Java/Scala development it's nothing new to get an issue. The tracker is represented by the simple servlet that handles 
Img.5.:: Lego Issue Tracker
GET and POST request and allows to share the issue with others (Img.5). 
Img.6.: Velocity templates for LegoIssueTracker
   For generating the Lego issue tracker front-end (Img.6.) we use Velocity server-side template language. 
In side every template file (edit.vm, list.vm or new.vm) is possible to employ JavaScript to make for example cross-domain JSONP calls and get information from another resources (in my case RaspberryPi powered by Netty)
example: new.vm or edit.vm
...
function getRaspData(){
  jQuery.ajax({
    url: 'IP_ADDRESS',
    dataType: 'JSONP',
    jsonpCallback: 'callback',
    type: 'GET',
    success: function (data) {
      ...
      jQuery('input[name="ip"]').val(data.ip);
    }
  });
}
...
AJS.$(document).ready(function() {
   jQuery('.button-rasp').click(function() {
      console.log('RASP PI');
      ...
      getRaspData();
   });
});
...
...
...edit.vm - Velocity template language ...
...
<h1> Edit Lego issue $issue.getKey()</h1> #if ($errors.size()>0) ...
Now we are ready, according to the atlassian descriptor file, to create the Servlet implementaiton:
public class IssueCRUD extends HttpServlet {
    ...
    private static final String PLUGIN_STORAGE_KEY = "com.example.plugins.tutorial.servlet.lego";
    private static final String NO_IP_MESSAGE = "No IP Setup";
    ...
    private IssueService issueService;
    private ProjectService projectService;
    private SearchService searchService;
    private UserManager userManager;
    private TemplateRenderer renderer;
    private LegoBrickService legoBrickService;
    private static final String LIST_BROWSER_TEMPLATE = "/templates/list.vm";
    private static final String NEW_BROWSER_TEMPLATE = "/templates/new.vm";
    ...
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ...
    }
    ...
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ...
        User user = getCurrentUser(req);
        ...
     }
 ... 
After short coding session we have prepared also LegoIssue sending functionality 
Img.7.:: send the 1st Lego Issue to the LEGO project
The REST API in JIRA is power by JAX-RS - Jersey. It's easy to expose. Let's move back to the Atlassian descriptor file and define the access path. The whole work is then strait forward in our example:Add3.:: Lego REST api 
@Path("/")
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public class RestLegoService {
…
   public RestLegoService(PluginSettingsFactory pluginSettingsFactory) {
        this.pluginSettingsFactory = pluginSettingsFactory;
    }
   @GET
   @Path("brick")
   public Response getLegoBrick() { 
   …
In the end we get appropriate JSON response by accessing the defined path. 

I hope this blog post helps in way to get a basic idea how to deal with Atlassian plugin development. In my case I don't want to use it as the reminder what I have done :)
Enjoy!
PS: I've used IntelliJ IDEA 13.x and SourceTree    
  

No comments: