====================== Introductory - Jira ====================== Introduction ------------ This tutorial provides instructions for the 4 step process to create a functional plugin called **SampleJira** which retrieves issues from a remote Atlassian JIRA server. The 4 Step Plugin creation Process ---------------------------------- 1. Create Plugin Workspace. 2. Define plugin spec and generate plugin. 3. Implement additional logic. 4. Build plugin and test. Prerequisites ------------- These are assumptions for this Tutorial. 1. An active CloudBees Flow instance. 2. Internet connection. 3. **flowdpk** installed and set up. In this tutorial we will use a public Jira instance of the Jenkins CI project, but you can use any Jira instance you have access to. Step 1 : Create Plugin Workspace --------------------------------- After making sure **flowpdk** is available in your PATH, you can create a plugin workspace. Change to your working directory :: cd ~/work Call **flowpdk** as follows :: flowpdk generate workspace In the interactive prompt type **SampleJira** as plugin name. For the rest of the options (except the plugin language) which are all optional, choose defaults if available or provide your own values. For the plugin language you should use 'groovy' .. figure:: images/tutorial-introductory-jira/workspaceGeneration.png :alt: Generated Workspace Generated Workspace The generated workspace would have the following 2 files: .. figure:: images/tutorial-introductory-jira/filesInWorkspace.png :alt: Files in Workspace Files in Workspace Step 2: Define plugin spec and generate plugin ---------------------------------------------- The pluginspec.yaml has 2 sections one for configuration and the other for procedures. Update configuration section of YAML ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ To interact with Jira we need the following config values: 1. Jira endpoint 2. Username for authorization. 3. Password for authorization. Replace the configuration section with following: .. code:: yaml # Plugin configuration description configuration: # A set of fields will be added to process debug level in the configuration hasDebugLevel: true parameters: - name: config documentation: The name for the created configuration required: true type: entry label: Configuration Name - name: desc documentation: Description for the configuration required: false type: entry label: Description - name: endpoint documentation: Third-party endpoint to connect to (Jira server URL), e.g. 'http://jira:8080/' required: true type: entry label: Endpoint - name: basic_credential documentation: Jira credentials required: false type: credential label: Jira Credential Rename the auto-generated specification called credential to basic_credential and set the 'required' attribute to false. Update procedures section ~~~~~~~~~~~~~~~~~~~~~~~~~~ We will implement a procedure called "GetIssue". Replace the procedures section with following: .. code:: yaml procedures: - name: 'GetIssue' description: 'Saves details for Jira issue to the properties' # configuration field will be generated automatically hasConfig: true shell: 'ec-groovy' parameters: - name: jiraKey documentation: Key of the issue to retrieve. required: true type: entry label: Jira Key outputParameters: issueStatus: 'Name of the status of the retrieved issue' Generate plugin ~~~~~~~~~~~~~~~ Execute the following command from the root level directory of the plugin workspace. :: flowpdk generate plugin After execution, it should look as follows. .. figure:: images/tutorial-introductory-jira/pluginGeneration.png :alt: Plugin Generation With this step, plugin generation is complete. Review the auto-generated code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This section is provided to give a perspective on how the boiler plate generated code looks like and what it means. The dsl/properties folder has the following structure: - groovy/lib - groovy/scripts - perl/core **Important: Do not modify any file under core folders. Core folders has plugin-related internal code, which should not be edited by plugin developer.** The only folder that could be modified is the groovy/lib folder. Notice that dsl/properties/groovy/lib/SampleJira.groovy has been auto-generated and contains the following code. .. code:: groovy import com.cloudbees.flowpdf.* /** * SampleJira */ class SampleJira extends FlowPlugin { @Override Map pluginInfo() { return [ pluginName : '@PLUGIN_KEY@', pluginVersion : '@PLUGIN_VERSION@', configFields : ['config'], configLocations: ['ec_plugin_cfgs'], defaultConfigValues: [:] ] } /** * getIssue - GetIssue/GetIssue * Add your code into this method and it will be called when the step runs * @param config (required: true) * @param jiraKey (required: true) */ def getIssue(StepParameters p, StepResult sr) { /* Log is automatically available from the parent class */ log.info( "getIssue was invoked with StepParameters", /* runtimeParameters contains both configuration and procedure parameters */ p.toString() ) Context context = getContext() // Setting job step summary to the config name sr.setJobStepSummary(p.getParameter('config').getValue() ?: 'null') sr.setReportUrl("Sample Report", 'https://cloudbees.com') sr.apply() log.info("step GetIssue has been finished") } // === step ends === } Each plugin procedure has one or more steps. The auto-generated code for the step “GetIssue” is located in dsl/procedures/GetIssue/steps/GetIssue.groovy and contains following: .. code:: groovy $[/myProject/groovy/scripts/preamble.groovy.ignore] SampleJira plugin = new SampleJira() plugin.runStep('GetIssue', 'GetIssue', 'getIssue') Step 3: Implement Additional logic ---------------------------------- Add necessary library imports ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ We will use a framework package named **com.cloudbees.flowpdf.client** to perform HTTP requests and **groovy.json.JsonOutput** to transform the received result to JSON. Add imports to the head of the SampleJira.groovy: .. code-block:: groovy :linenos: import com.cloudbees.flowpdf.* import com.cloudbees.flowpdf.client.* import groovy.json.JsonOutput Add default config values: ~~~~~~~~~~~~~~~~~~~~~~~~~~ Edit pluginInfo function to set default values for parameters. Notice that authScheme is set to the value **basic**. .. code:: groovy @Override Map pluginInfo() { return [ pluginName : '@PLUGIN_KEY@', pluginVersion : '@PLUGIN_VERSION@', configFields : ['config'], configLocations: ['ec_plugin_cfgs'], defaultConfigValues: [ authScheme : 'basic' ] ] } Modify GetIssue step code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The auto-generated function in step looks as follows: .. code:: groovy /** * getIssue - GetIssue/GetIssue * Add your code into this method and it will be called when the step runs * @param config (required: true) * @param jiraKey (required: true) */ def getIssue(StepParameters p, StepResult sr) { /* Log is automatically available from the parent class */ log.info( "getIssue was invoked with StepParameters", /* runtimeParameters contains both configuration and procedure parameters */ p.toString() ) Context context = getContext() // Setting job step summary to the config name sr.setJobStepSummary(p.getParameter('config').getValue() ?: 'null') sr.setReportUrl("Sample Report", 'https://cloudbees.com') sr.apply() log.info("step GetIssue has been finished") } // === step ends === Modify the logic as follows: .. code:: groovy /** * getIssue - GetIssue/GetIssue * Add your code into this method and it will be called when the step runs * @param config (required: true) * @param jiraKey (required: true) */ def getIssue(StepParameters p, StepResult sr) { /* Log is automatically available from the parent class */ log.info( "getIssue was invoked with StepParameters", /* runtimeParameters contains both configuration and procedure parameters */ p.toString() ) String issueIdentifier = p.getRequiredParameter('jiraKey').getValue() def requestParams = [ method: 'GET', path : sprintf('/rest/api/2/issue/%s', issueIdentifier), query : [ fields: 'summary,comment,status' ] ] // Initializing the client and the request def restClient = getContext().newRESTClient() def request = restClient.newRequest(requestParams) // Executing the request def issue = restClient.doRequest(request) log.debug("Response: " + issue.dump()) // Saving issue as a JSON def jsonIssuesStr = JsonOutput.toJson(issue) log.debug("Issue JSON: " + jsonIssuesStr) sr.setOutcomeProperty('/myCall/issue', jsonIssuesStr) String issueStatus = issue?.fields?.status?.name ?: 'no status' log.info("Issue status is: ${issueStatus}") sr.apply() log.infoDiag("step GetIssue has been finished") } // === step ends === Set Output Parameters ~~~~~~~~~~~~~~~~~~~~~ An output parameter is set as part of the StepResult object. Add the following code inside of the step function in SampleJira.groovy before the sr.apply() call. .. code:: groovy sr.setOutputParameter('issueStatus', issueStatus) Set pipeline, job and jobStep summary ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Set all the 3 summaries - pipeline summary, job summary and jobStep summary. Add the following code inside of the step function in SampleJira.groovy before the sr.apply() call. .. code:: groovy sr.setPipelineSummary("Issue number", issueIdentifier) sr.setJobSummary("Issue status is ${issueStatus}") sr.setJobStepSummary("Saved json representation of ${issueIdentifier}") Review SampleJira.groovy ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is a summary of how SampleJira.groovy looks like after you make the changes. .. code-block:: groovy :linenos: import com.cloudbees.flowpdf.* import com.cloudbees.flowpdf.client.* import groovy.json.JsonOutput /** * SampleJira */ class SampleJira extends FlowPlugin { @Override Map pluginInfo() { return [ pluginName : '@PLUGIN_KEY@', pluginVersion : '@PLUGIN_VERSION@', configFields : ['config'], configLocations: ['ec_plugin_cfgs'], defaultConfigValues: [ authScheme : 'basic' ] ] } /** * getIssue - GetIssue/GetIssue * Add your code into this method and it will be called when the step runs * @param config (required: true) * @param jiraKey (required: true) */ def getIssue(StepParameters p, StepResult sr) { /* Log is automatically available from the parent class */ log.info( "getIssue was invoked with StepParameters", /* runtimeParameters contains both configuration and procedure parameters */ p.toString() ) String issueIdentifier = p.getRequiredParameter('jiraKey').getValue() def requestParams = [ method: 'GET', path : sprintf('/rest/api/2/issue/%s', issueIdentifier), query : [ fields: 'summary,comment,status' ] ] // Initializing the client and the request def restClient = getContext().newRESTClient() def request = restClient.newRequest(requestParams) // Executing the request def issue = restClient.doRequest(request) log.debug("Response: " + issue.dump()) // Saving issue as a JSON def jsonIssuesStr = JsonOutput.toJson(issue) log.debug("Issue JSON: " + jsonIssuesStr) sr.setOutcomeProperty('/myCall/issue', jsonIssuesStr) String issueStatus = issue?.fields?.status?.name ?: 'no status' log.info("Issue status is: ${issueStatus}") // Setting outputs sr.setOutputParameter('issueStatus', issueStatus) sr.setPipelineSummary("Issue number", issueIdentifier) sr.setJobSummary("Issue status is ${issueStatus}") sr.setJobStepSummary("Saved json representation of ${issueIdentifier}") sr.apply() log.infoDiag("step GetIssue has been finished") } // === step ends === } Step 4: Build, Install and Test ------------------------------- Build the plugin from root directory of the SampleJira plugin workspace. :: flowpdk build It should look something like this: .. figure:: images/tutorial-introductory-jira/pluginBuild.png Congratulations! You have now created a functional plugin. Install and Promote the plugin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Go to your CloudBees Flow instance, login and navigate to the Plugin Manager page which lists all plugins. .. figure:: images/tutorial-introductory-jira/pluginsListLink.png Click on "Install from File/URL" tab, click Choose File, select your build/SampleJira.zip file and click upload: .. figure:: images/tutorial-introductory-jira/installPlugin.png You will be redirected to the plugin manager page. Find your plugin SampleJira, and click "Promote" link. After promotion the page would be automatically refreshed. Create a Pipeline and Configure the plugin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ After your plugin is installed, navigate to the Pipelines page and click "New" in the top right corner of the web page. .. figure:: images/tutorial-introductory-jira/createNewPipeline.png Click "Create New", enter "Sample Jira Pipeline", choose your project, click "Ok". Now you're in your pipeline editor. Click "Add +" on the task explorer, enter "GetIssue", click "Select Task Type", scroll down the plugin list and click on your GetIssue procedure below the plugin name: .. figure:: images/tutorial-introductory-jira/selectProcedure.png Now click on your procedure name and "Define" link. You will see: .. figure:: images/tutorial-introductory-jira/defineProcedureLink.png Click on input parameters to confirm that this procedure sees all the parameters as per the YAML spec. Provide "JENKINS-1" as a value for the "Jira Key" parameter. .. figure:: images/tutorial-introductory-jira/inputParameters.png Click on the triangle at the right of "Configuration Name" parameter input field and choose the "New Configuration" item. For this showcase we are using Jenkins-CI public issue tracker. Provide a meaningful name and description for the configuration. Use 'https://issues.jenkins-ci.org/' as an Endpoint. This public tracker doesn't require authentication to browse issues, so we can leave the credential field empty. .. figure:: images/tutorial-introductory-jira/configValues.png Save configuration and select it on the Input Parameters form. This concludes the plugin as well as the pipeline setup. Running the pipeline ~~~~~~~~~~~~~~~~~~~~ In the flow pipeline page click "Run" on the pipeline you have created. In the Popup menu, click "New Run" and in the modal dialog that shows up, click "Run" again. You will see the following: .. figure:: images/tutorial-introductory-jira/pipelineResult1.png Click on the summary link: .. figure:: images/tutorial-introductory-jira/pipelineResult2.png Click on the "1. GetIssue". You can see that the special step 'flowpdk-setup' was performed to transfer the dependencies. .. figure:: images/tutorial-introductory-jira/pipelineResult3.png Click on the "Parameters" tab to see the output parameter: .. figure:: images/tutorial-introductory-jira/pipelineResult4.png Click on the "Properties" tab to see the JSON representation in the "issue" job property: .. figure:: images/tutorial-introductory-jira/pipelineResult4_properties.png Click back on the "Steps" link and open the "GetIssue" step log link: .. figure:: images/tutorial-introductory-jira/pipelineResult5.png We now have a minimal working plugin for Jira. Summary ------- The following diagram is provided as a reference to enunciate the flow of control using **flowpdf-groovy**. .. figure:: images/tutorial-introductory-jira/flowpdf-lib-groovy-run-flow.png Used Libraries: --------------- - `Package com.cloudbees.flowpdf.client `_.