Overview

I found myself with an interesting Jira problem today.  We had a dashboard that wasn’t showing all issues relevant to the JQL query in the filter.   The issues were fully present in the system, but would only appear if we went in after creation and tweaked them in some way.
Essentially we had issues that weren’t being picked up by the filter because the system didn’t see them as “complete”.

Here’s the sanitized JQL:

 project = <project> AND status = Queued AND "<Custom Field>" = "<value>" 

It was picking up some of the issues, remember. And the issues were all created in the same way: they came in through the Service Desk as Service Requests. So the JQL wasn’t the issue.

Trying Different Solutions

I already had a listener running as part of the process, so I tried adding an issueUpdate statement to it:

 issueManager.updateIssue(user, issue, EventDispatchOption.ISSUE_UPDATED, false) 

This did not resolve the issue.   I next tried updating the issue summary to be itself as part of the Workflow Transition process:

 issue.setSummary(issue.summary + " - " + issue.reporter.displayName) 

This also did not resolve the issue.

Resolving the Issue

In the end I solved the issue by introducing a re-index

Introduction

Once upon a time I spent a week writing a script to automate some functionality for Jira.  The script was supposed to assign issues based on certain criteria, and also update a custom field that contained the name of the team that was currently responsible for the ticket.

At the end of the week I had the script working according to the specifications.   We tested it on Friday afternoon, and all was well.
On Monday morning the project manager sent me an email, notifying me that the script now had to work a completely different way.  It also had to be finished by the end of that day, as Tuesday was the day they were using the script as part of a training session.

Updating the Script

The good news was that for the most part, the changes I had to make to the script were reductive. It had to do fewer things.   However, I had initially included the Custom Field update as part of the transition:

    def issueInputParameters = issueService.newIssueInputParameters()
    issueInputParameters.addCustomFieldValue(customFieldID, customFieldValue)
    //The request is for a new workstation, so set Next to Act to Tier 1 - IDIR Services
    issueInputParameters.setSummary(issue.summary + " - " + issue.getReporterUser().displayName)
    //Set 

I have series of ProForma forms that are submitted as issues to the Jira Service Desk.

I needed to run some scripts against these issues after they were created and fast-tracked into a Queued state.  I elected to run a Groovy script as a Listener on Issue Update. The thinking was that because the state of the ticket was being updated from Open to Queued, the Listener would have plenty of material to work with.

The issue that I encountered was that the listener was only detecting some of the issues.  I enabled logging at the outset of the Listener script and told it to record any ticket that was subject to an Issue Updated event.  Some of the issues created by the forms weren’t being detected by this at all. At the same time, some of them were.

The issue was consistent, in that certain forms were never detected by the listener, and certain forms were always detected.  There was no appreciable difference in the form setup or the way the tickets were processed.  The Listener script itself simply examined the Request Type field of each ticket (which is a Jira Core field), and routed the ticket based on

 

During the process of writing a listener for Jira, I found myself encountering a strange error.  The error looked like this:

com.atlassian.servicedesk.api.AuthorizationException: The action performed required a logged in user. Please log in and try again.

This was strange for two reasons. First, I am a Jira administrator with total access to the entire instance. Second, my script had explicitly supplied the logged-in user (myself) as the account under which to run the script.

What gives?

The code I used to supply my own account to the script looks like so:

 def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
 

This solution had been more than sufficient previously.   I searched and I searched and I couldn’t find anything related to my issue.  If you search the text of the error, three results are returned, and they’re about REST API permissions. Not applicable here.

In the end I accidentally stumbled on the answer, by trying different solutions for the action I was trying to take.  What I was trying to do was get the Request Type of the issue in question.

The solution was to explicitly provide an account to the script, under which the method could run.   That is, I changed the code to:

I found myself in a situation wherein a ProForma form on the Jira Service Desk contained a custom field.  I needed the contents of that field to dictate which team the resulting issue was assigned to.

The first thing I tried was to add a Groovy script to a transition in the Service Desk Workflow.   If I could tell the script to transition the issue based on the value of a field, the issue should be resolved.

This turned out to not be possible. For some reason, the value of a custom field is not populated from the form to the field until after the ticket “lands”, or finishes moving through the workflow.  It’s not enough to simply trigger the script after the Open/Create transition.   No matter what, the value of the custom field is not available until after all of the initial transitions have finished.  No matter how you try to reference the custom field, the value returned will always be NULL.

This was extremely aggravating, especially when it seems like such a simple solution SHOULD work, and it’s not clear whether the issue lies with your script or with the system.

The solution you’re going to come across

Introduction

Jira Issues can only be transition between states in a manner that resembles the Workflow of the parent project. In other words, before you begin trying to script the transition of a Jira issue, you must understand the workflow and what the available transitions actually are.

Further to that point, there’s more to transitions that simply changing the status of an issue. Some transitions have criteria.  For example, you may want to move an issue from Open to Pending.  In order to do so, you may need to select a Pending State, and add a comment.  How do you account for that in a script?

Using Groovy and ScriptRunner to transition Jira issues is a pretty straightforward process. So too is this process quite simple if you need to include transition criteria.  It’s simple, if you can find a guide on how to do it. As with most of the things I blog about, I couldn’t find instructions for accomplishing this simple task, so I’m writing my own.

Overview

The script we’re going to explore transitions a Jira issue from one state to another, and fills in the criteria required to make the transition a valid one.  As

It is entirely possible to set up Jira so that a subtask may remain open, while the parent task is closed. This effectively creates orphan subtasks, not connected to any open issue or ticket.

Identifying these is a matter of first identifying all subtasks, and then checking the status of both the subtask and its parent.

We first identify all subtasks for a given project by invoking a service context, and running some JQL against the Jira instance:

 import com.atlassian.jira.bc.filter.SearchRequestService
import com.atlassian.jira.issue.search.SearchRequest
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.jira.bc.JiraServiceContext
import com.atlassian.jira.bc.JiraServiceContextImpl
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.component.ComponentAccessor
import com.atlassian.jira.issue.search.SearchProvider
import com.atlassian.jira.jql.parser.JqlQueryParser
import com.atlassian.jira.web.bean.PagerFilter
import com.atlassian.jira.bc.issue.search.SearchService
import com.atlassian.jira.issue.label.LabelManager

def jqlQueryParser = ComponentAccessor.getComponent(JqlQueryParser)
def searchProvider = ComponentAccessor.getComponent(SearchProvider)
def issueManager = ComponentAccessor.getIssueManager()
def searchService = ComponentLocator.getComponent(SearchService)
def user = ComponentAccessor.getJiraAuthenticationContext().getLoggedInUser()
def searchManager = ComponentLocator.getComponent(SearchRequestService)
def contextManager = ComponentLocator.getComponent(JiraServiceContext)
def searchRequest = ComponentLocator.getComponent(SearchRequest)
def labelManager = ComponentLocator.getComponent(LabelManager)



JiraServiceContextImpl serviceCtx = new JiraServiceContextImpl(user);
//Declare a search context using the logged-in user

def queryParser = ComponentAccessor.getComponent(JqlQueryParser)
//Declare a parser to handle the JQL query

def query = queryParser.parseQuery('project = "<project name>" ')
//Define the JQL query.  In this instance we're returning all issues under a given project

def search = searchService.search(user, query, PagerFilter.getUnlimitedFilter())
//Define a search, using all the pieces defined 

This is part three of my series on using Python to connect to the Twitter API.

Imagine for a moment that you had a specific vision for your Twitter account.  A vision of balance, and harmony.  What if you only followed people who also followed you?  Whether or not you want to curate your Twitter experience in this transactional way is entirely up to you. It’s your account!

We can do that with Python. As always, replace the placeholders with your own account credentials.  See Part One of this series if you’re not sure how to do that. 

Let’s take a look at the code required to do this:

 import tweepy

consumerKey = "<>"
consumerSecret = "<>"
accessToken = "<>"
accessTokenSecret = "<>"

auth = tweepy.OAuthHandler(consumerKey, consumerSecret)
auth.set_access_token(accessToken, accessTokenSecret)

#Call the api 
api = tweepy.API(auth,wait_on_rate_limit=True)

#define two empty lists
followers = []
following = []
  
#Get the list of friends
for status in tweepy.Cursor(api.get_friends,count=200).items():
    #Add the results to the list
    following.append(status.screen_name)

#get the list of followers
for status in tweepy.Cursor(api.get_followers,count=200).items():
    #Add the results to the list
    followers.append(status.screen_name)
    
#compare the lists and take action
for person in following:
    if person not in followers:
        api.destroy_friendship(screen_name=person)
        print("Unfollowed " + person)
         

As

Introduction

There are a great number of things that you might want to do with Twitter, for which the web or mobile clients don’t have facilities.  For example, you might want to run a script that automatically thanks anyone who follows you.  Or you might want to run a script that Likes any comment that someone adds to your post. 
It is worth noting that Twitter is extremely strict when it comes to automated actions around followers.  For example, it’s entirely possible to scrape the follower list of large accounts, and write a script to automatically follow all of those people. That would conceivably get you a large number of followers, and when you were done you could just write anther script to unfollow anyone who didn’t follow you back.   I promise that the Twitter API will pick up on what you’re doing, and put you in Twitter Jail.  Don’t do that.
In this post we’ll examine how to make a basic connection to the Twitter API, using Python and Tweepy.    We’ll investigate one of the errors you might encounter, and discuss the pagination of the results that the API returns. The example we’re using will return a list

Introduction

A great deal of the available information regarding the use of Twitter and Python is outdated. The Twitter API has undergone several major revisions in the last few years, and many of the available tutorials now only lead to frustration.

Not only has the API undergone major revisions, but there are multiple supported versions of the API.  Some methods referenced by online tutorials will only work with certain other methods!

My hope for this series is to provide a clear and concise tutorial for connecting to the Twitter API using Tweepy and Python.

In order to connect to the Twitter API, your account must be provisioned for Developer access.  This is a free service, at the basic level, but does require additional setup.  That will be the focus of this first blog post.

Resources Required

You will need:

  • A Twitter account with a verified email address and verified phone number
  • Developer access to that account
  • A Python IDE (I use Spyder)
  • Tweepy

This post focuses solely on gaining Developer access, and assumes you already have the account.

The Setup

While I intend for this tutorial to be quite detailed, I trust that you can handle signing up for a