Hi,
We need to migrate from our site to merge with another site.
During the migration, some custom fields and other data will be renamed.
Consequently, many filters will break and any boards, dashboards, plans, etc. will break too.
We have over 22,000 filters. Manually checking all our filters for jql that will be affected by renaming is not feasible. So, I need a way to list all the filters (not just the ones shared to the public), including filter name, id, jql, owner and when last used.
I tried the Jira API but that only returns filters that are shared publicly even though I am a site-admin.
Any suggestions?
Hello @Adam Sterrie ,
Good day! Welcome to Atlassian Community :)
Currently, in Jira cloud, on UI, its not possible to get all the details. However you can use REST API to get certain details like JQL, filter ID, Filter owner and Filter name.
https://your-domain.atlassian.net/rest/api/3/filter/search?expand=owner,jql
There are few feature requests for the same:
However if you need data for reference purposes before site migration, you can contact our support team here: https://support.atlassian.com/contact/
They will help you by exporting all the details from the backend database.
Thank you
Hello @Adam Sterrie , you can try with this Python code:
import requests
from requests.auth import HTTPBasicAuth
import json
from datetime import datetime
import sys
# ============================================================================
# CONFIGURATION - Update these variables with your Jira credentials
# ============================================================================
JIRA_URL = "" # Your Jira instance URL
JIRA_EMAIL = "" # Your Jira email address
JIRA_API_TOKEN = "" # Your Jira API token
# ============================================================================
def get_all_filters(jira_url, auth):
"""
Fetch all filters accessible to the user.
Args:
jira_url: Base Jira URL
auth: HTTPBasicAuth object
Returns:
List of filter dictionaries
"""
filters = []
start_at = 0
max_results = 50
while True:
# API endpoint to get filters
url = f"{jira_url}/rest/api/3/filter/search"
params = {
'startAt': start_at,
'maxResults': max_results,
'expand': 'jql,owner,sharePermissions'
}
try:
response = requests.get(url, auth=auth, params=params)
response.raise_for_status()
data = response.json()
filters.extend(data.get('values', []))
# Check if there are more results
if start_at + max_results >= data.get('total', 0):
break
start_at += max_results
except requests.exceptions.RequestException as e:
print(f"Error fetching filters: {e}", file=sys.stderr)
if hasattr(e, 'response') and e.response is not None:
print(f"Response: {e.response.text}", file=sys.stderr)
sys.exit(1)
return filters
def format_last_viewed(last_viewed):
"""
Format the last viewed timestamp.
Args:
last_viewed: ISO format timestamp string or None
Returns:
Formatted date string or 'Never'
"""
if not last_viewed:
return 'Never'
try:
dt = datetime.fromisoformat(last_viewed.replace('Z', '+00:00'))
return dt.strftime('%Y-%m-%d %H:%M:%S')
except Exception:
return last_viewed
def is_public_filter(filter_data):
"""
Check if a filter is shared publicly.
Args:
filter_data: Filter dictionary
Returns:
Boolean indicating if filter is public
"""
share_permissions = filter_data.get('sharePermissions', [])
for permission in share_permissions:
if permission.get('type') == 'global':
return True
return False
def print_filters_table(filters):
"""
Print filters in a formatted table.
Args:
filters: List of filter dictionaries
"""
if not filters:
print("No filters found.")
return
# Print header
print(f"\n{'ID':<10} {'Name':<40} {'Owner':<30} {'Public':<8} {'Last Viewed':<20}")
print("=" * 108)
# Print each filter
for f in filters:
filter_id = f.get('id', 'N/A')
name = f.get('name', 'N/A')[:39] # Truncate if too long
owner_name = f.get('owner', {}).get('displayName', 'N/A')[:29]
is_public = 'Yes' if is_public_filter(f) else 'No'
last_viewed = format_last_viewed(f.get('viewUrl')) # Note: lastViewed may not be available
print(f"{filter_id:<10} {name:<40} {owner_name:<30} {is_public:<8} {last_viewed:<20}")
print(f"\nTotal filters: {len(filters)}")
def print_filters_detailed(filters):
"""
Print detailed information for each filter.
Args:
filters: List of filter dictionaries
"""
if not filters:
print("No filters found.")
return
for i, f in enumerate(filters, 1):
print(f"\n{'='*80}")
print(f"Filter #{i}")
print(f"{'='*80}")
print(f"ID: {f.get('id', 'N/A')}")
print(f"Name: {f.get('name', 'N/A')}")
print(f"Description: {f.get('description', 'N/A')}")
print(f"Owner: {f.get('owner', {}).get('displayName', 'N/A')} ({f.get('owner', {}).get('emailAddress', 'N/A')})")
print(f"JQL: {f.get('jql', 'N/A')}")
print(f"Public: {'Yes' if is_public_filter(f) else 'No'}")
print(f"Favorite: {f.get('favourite', False)}")
print(f"View URL: {f.get('viewUrl', 'N/A')}")
print(f"Search URL: {f.get('searchUrl', 'N/A')}")
def export_to_json(filters, filename='jira_filters.json'):
"""
Export filters to JSON file.
Args:
filters: List of filter dictionaries
filename: Output filename
"""
try:
with open(filename, 'w', encoding='utf-8') as f:
json.dump(filters, f, indent=2, ensure_ascii=False)
print(f"\nFilters exported to {filename}")
except Exception as e:
print(f"Error exporting to JSON: {e}", file=sys.stderr)
def export_to_csv(filters, filename='jira_filters.csv'):
"""
Export filters to CSV file.
Args:
filters: List of filter dictionaries
filename: Output filename
"""
try:
import csv
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
# Write header
writer.writerow(['ID', 'Name', 'Owner Name', 'Owner Email', 'JQL', 'Public', 'Favorite', 'Description', 'View URL'])
# Write data
for filter_data in filters:
writer.writerow([
filter_data.get('id', ''),
filter_data.get('name', ''),
filter_data.get('owner', {}).get('displayName', ''),
filter_data.get('owner', {}).get('emailAddress', ''),
filter_data.get('jql', ''),
'Yes' if is_public_filter(filter_data) else 'No',
filter_data.get('favourite', False),
filter_data.get('description', ''),
filter_data.get('viewUrl', '')
])
print(f"\nFilters exported to {filename}")
except Exception as e:
print(f"Error exporting to CSV: {e}", file=sys.stderr)
def main():
"""
Main function to execute the script.
"""
print("Jira Filters Listing Tool")
print("=" * 80)
# Validate credentials are configured
if JIRA_URL == "https://your-domain.atlassian.net" or \
JIRA_EMAIL == "your-email@example.com" or \
JIRA_API_TOKEN == "your-api-token-here":
print("\nERROR: Please update the configuration variables at the top of the script:")
print(" - JIRA_URL")
print(" - JIRA_EMAIL")
print(" - JIRA_API_TOKEN")
sys.exit(1)
# Create authentication
auth = HTTPBasicAuth(JIRA_EMAIL, JIRA_API_TOKEN)
# Fetch all filters
print("\nFetching filters...")
filters = get_all_filters(JIRA_URL, auth)
# Sort filters by name
filters.sort(key=lambda x: x.get('name', '').lower())
# Display options
print(f"\nFound {len(filters)} filters.")
print("\nDisplay options:")
print("1. Table view (summary)")
print("2. Detailed view")
print("3. Export to JSON")
print("4. Export to CSV")
print("5. All of the above")
choice = input("\nEnter your choice (1-5): ").strip()
if choice == '1' or choice == '5':
print_filters_table(filters)
if choice == '2' or choice == '5':
print_filters_detailed(filters)
if choice == '3' or choice == '5':
export_to_json(filters)
if choice == '4' or choice == '5':
export_to_csv(filters)
if choice not in ['1', '2', '3', '4', '5']:
print("Invalid choice. Showing table view by default.")
print_filters_table(filters)
if __name__ == "__main__":
main()
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thank you. This looks great but I am being limited to using curl only - no Python. Plus how do you get round the permission limitation on the API call "https://${JIRA_SITE}.atlassian.net/rest/api/3/filter/search" that does not return all filters?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You need to be a Jira admin to see all of the filters. For Curl, you can try:
============================================================================
# CONFIGURATION - Update these variables with your Jira credentials
# ============================================================================
JIRA_URL="https://your-domain.atlassian.net" # Your Jira instance URL
JIRA_EMAIL="your-email@example.com" # Your Jira email address
JIRA_API_TOKEN="your-api-token-here" # Your Jira API token
# ============================================================================
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to check if required commands are available
check_requirements() {
local missing_tools=()
if ! command -v curl &> /dev/null; then
missing_tools+=("curl")
fi
if ! command -v jq &> /dev/null; then
missing_tools+=("jq")
fi
if [ ${#missing_tools[@]} -ne 0 ]; then
echo -e "${RED}Error: Missing required tools: ${missing_tools[*]}${NC}"
echo "Please install them:"
echo " - Ubuntu/Debian: sudo apt-get install curl jq"
echo " - MacOS: brew install curl jq"
echo " - RHEL/CentOS: sudo yum install curl jq"
exit 1
fi
}
# Function to validate configuration
validate_config() {
if [ "$JIRA_URL" = "https://your-domain.atlassian.net" ] || \
[ "$JIRA_EMAIL" = "your-email@example.com" ] || \
[ "$JIRA_API_TOKEN" = "your-api-token-here" ]; then
echo -e "${RED}ERROR: Please update the configuration variables at the top of the script:${NC}"
echo " - JIRA_URL"
echo " - JIRA_EMAIL"
echo " - JIRA_API_TOKEN"
exit 1
fi
}
# Function to fetch all filters with pagination
fetch_all_filters() {
local all_filters="[]"
local start_at=0
local max_results=50
local total=0
local is_last=false
echo -e "${BLUE}Fetching filters...${NC}"
while [ "$is_last" = "false" ]; do
# Make API request
response=$(curl -s -w "\n%{http_code}" \
-u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
-H "Accept: application/json" \
"${JIRA_URL}/rest/api/3/filter/search?startAt=${start_at}&maxResults=${max_results}&expand=jql,owner,sharePermissions")
# Extract HTTP status code
http_code=$(echo "$response" | tail -n1)
body=$(echo "$response" | sed '$d')
# Check for errors
if [ "$http_code" != "200" ]; then
echo -e "${RED}Error: HTTP $http_code${NC}"
echo "$body" | jq -r '.errorMessages[]? // .message? // .'
exit 1
fi
# Get total count on first request
if [ $start_at -eq 0 ]; then
total=$(echo "$body" | jq -r '.total')
echo -e "${GREEN}Found $total filters${NC}"
fi
# Extract values and append to all_filters
current_filters=$(echo "$body" | jq -r '.values')
all_filters=$(echo "$all_filters" | jq --argjson new "$current_filters" '. + $new')
# Check if this is the last page
is_last=$(echo "$body" | jq -r '.isLast')
# Update start_at for next iteration
start_at=$((start_at + max_results))
# Progress indicator
fetched=$(echo "$all_filters" | jq 'length')
echo -ne "\rFetched: $fetched/$total filters"
done
echo # New line after progress
echo "$all_filters"
}
# Function to check if filter is public
is_public() {
local filter_json="$1"
echo "$filter_json" | jq -r '
if (.sharePermissions | any(.type == "global")) then
"Yes"
else
"No"
end
'
}
# Function to display filters in table format
display_table() {
local filters="$1"
echo ""
echo "==============================================================================================================="
printf "%-10s %-40s %-30s %-8s %-20s\n" "ID" "Name" "Owner" "Public" "Favorite"
echo "==============================================================================================================="
echo "$filters" | jq -r '.[] |
[
.id,
(.name | if length > 39 then .[0:36] + "..." else . end),
((.owner.displayName // "N/A") | if length > 29 then .[0:26] + "..." else . end),
(if (.sharePermissions | any(.type == "global")) then "Yes" else "No" end),
(if .favourite then "Yes" else "No" end)
] | @tsv' | \
while IFS=$'\t' read -r id name owner public favorite; do
printf "%-10s %-40s %-30s %-8s %-20s\n" "$id" "$name" "$owner" "$public" "$favorite"
done
total=$(echo "$filters" | jq 'length')
echo ""
echo "Total filters: $total"
echo ""
}
# Function to display detailed filter information
display_detailed() {
local filters="$1"
echo "$filters" | jq -r 'to_entries[] |
"
================================================================================
Filter #\(.key + 1)
================================================================================
ID: \(.value.id)
Name: \(.value.name)
Description: \(.value.description // "N/A")
Owner: \(.value.owner.displayName // "N/A") (\(.value.owner.emailAddress // "N/A"))
Email: \(.value.owner.emailAddress // "N/A")
JQL: \(.value.jql // "N/A")
Public: \(if (.value.sharePermissions | any(.type == "global")) then "Yes" else "No" end)
Favorite: \(if .value.favourite then "Yes" else "No" end)
View URL: \(.value.viewUrl // "N/A")
Search URL: \(.value.searchUrl // "N/A")
"'
}
# Function to export to JSON
export_json() {
local filters="$1"
local filename="${2:-jira_filters.json}"
echo "$filters" | jq '.' > "$filename"
echo -e "${GREEN}Filters exported to $filename${NC}"
}
# Function to export to CSV
export_csv() {
local filters="$1"
local filename="${2:-jira_filters.csv}"
# Write CSV header
echo "ID,Name,Owner Name,Owner Email,JQL,Public,Favorite,Description,View URL" > "$filename"
# Write data
echo "$filters" | jq -r '.[] |
[
.id,
.name,
(.owner.displayName // ""),
(.owner.emailAddress // ""),
(.jql // ""),
(if (.sharePermissions | any(.type == "global")) then "Yes" else "No" end),
(if .favourite then "Yes" else "No" end),
(.description // ""),
(.viewUrl // "")
] | @csv' >> "$filename"
echo -e "${GREEN}Filters exported to $filename${NC}"
}
# Function to export filters with full JQL to separate files
export_jql_files() {
local filters="$1"
local output_dir="${2:-jira_filters_jql}"
# Create output directory
mkdir -p "$output_dir"
# Export each filter's JQL to a separate file
echo "$filters" | jq -r '.[] |
"\(.id)|\(.name)|\(.jql // "")"' | \
while IFS='|' read -r id name jql; do
if [ -n "$jql" ]; then
# Sanitize filename
safe_name=$(echo "$name" | tr -cs '[:alnum:]-_' '_' | sed 's/_*$//')
filename="${output_dir}/${id}_${safe_name}.jql"
echo "$jql" > "$filename"
fi
done
total=$(find "$output_dir" -name "*.jql" 2>/dev/null | wc -l)
echo -e "${GREEN}Exported $total JQL files to $output_dir/${NC}"
}
# Main function
main() {
echo "================================================================================"
echo " Jira Filters Listing Tool "
echo "================================================================================"
echo ""
# Check requirements
check_requirements
# Validate configuration
validate_config
# Fetch all filters
filters=$(fetch_all_filters)
# Sort filters by name
filters=$(echo "$filters" | jq 'sort_by(.name | ascii_downcase)')
# Display menu
echo ""
echo "Display options:"
echo "1. Table view (summary)"
echo "2. Detailed view"
echo "3. Export to JSON"
echo "4. Export to CSV"
echo "5. Export JQL to separate files"
echo "6. All of the above"
echo ""
read -p "Enter your choice (1-6): " choice
case $choice in
1)
display_table "$filters"
;;
2)
display_detailed "$filters"
;;
3)
export_json "$filters"
;;
4)
export_csv "$filters"
;;
5)
export_jql_files "$filters"
;;
6)
display_table "$filters"
display_detailed "$filters"
export_json "$filters"
export_csv "$filters"
export_jql_files "$filters"
;;
*)
echo -e "${YELLOW}Invalid choice. Showing table view by default.${NC}"
display_table "$filters"
;;
esac
}
# Run main function
main
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Thanks again. I need to adapt to our environment and get back to you.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You need to query the DB—no workaround for that. APIs won't give you the full view.
You can start here:
https://support.atlassian.com/jira/kb/jira-get-list-of-all-filters-shared-with-everyone/
SELECT s.filtername, s.authorname, s.DESCRIPTION FROM searchrequest s
and the table with all filters is called: searchrequest
Hope that helps!
Regards
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.