Last active
December 20, 2024 17:15
-
-
Save moosh3/6f96b5380fd9e5c43e6ab1f5419bd70a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env node | |
const fetch = (...args) => import('node-fetch').then(({default: fetch}) => fetch(...args)); | |
class JiraClient { | |
constructor(baseUrl, email, apiToken) { | |
this.baseUrl = baseUrl; | |
this.auth = Buffer.from(`${email}:${apiToken}`).toString('base64'); | |
} | |
async getTicket(ticketId) { | |
try { | |
const response = await fetch(`${this.baseUrl}/rest/api/3/issue/${ticketId}`, { | |
headers: { | |
'Authorization': `Basic ${this.auth}`, | |
'Accept': 'application/json' | |
} | |
}); | |
if (!response.ok) { | |
throw new Error(`Failed to fetch ticket: ${response.statusText}`); | |
} | |
return await response.json(); | |
} catch (error) { | |
console.error(`Error fetching ticket ${ticketId}:`, error); | |
throw error; | |
} | |
} | |
} | |
class JiraSummarizer { | |
constructor(jiraClient) { | |
this.jiraClient = jiraClient; | |
} | |
async generateTicketSummary(ticketId) { | |
try { | |
const ticket = await this.jiraClient.getTicket(ticketId); | |
return { | |
key: ticket.key, | |
summary: ticket.fields.summary, | |
status: ticket.fields.status.name, | |
priority: ticket.fields.priority.name, | |
assignee: ticket.fields.assignee?.displayName || 'Unassigned', | |
created: new Date(ticket.fields.created).toLocaleDateString(), | |
updated: new Date(ticket.fields.updated).toLocaleDateString(), | |
description: this.summarizeDescription(ticket.fields.description), | |
comments: this.summarizeComments(ticket.fields.comment) | |
}; | |
} catch (error) { | |
console.error('Error generating ticket summary:', error); | |
throw error; | |
} | |
} | |
summarizeDescription(description) { | |
if (!description) return 'No description provided'; | |
// Extract main points from description | |
const sentences = description.split(/[.!?]+/); | |
return sentences.slice(0, 3).join('. ') + (sentences.length > 3 ? '...' : ''); | |
} | |
summarizeComments(comments) { | |
if (!comments?.comments?.length) return 'No comments'; | |
return comments.comments.slice(-3).map(comment => ({ | |
author: comment.author.displayName, | |
created: new Date(comment.created).toLocaleDateString(), | |
body: this.summarizeDescription(comment.body) | |
})); | |
} | |
} | |
// Check for required environment variables | |
const requiredEnvVars = ['JIRA_URL', 'JIRA_EMAIL', 'JIRA_API_TOKEN']; | |
const missingEnvVars = requiredEnvVars.filter(varName => !process.env[varName]); | |
if (missingEnvVars.length > 0) { | |
console.error('Error: Missing required environment variables:', missingEnvVars.join(', ')); | |
console.error('Please set these environment variables before running the script.'); | |
console.error('Example:'); | |
console.error('export JIRA_URL="https://your-domain.atlassian.net"'); | |
console.error('export JIRA_EMAIL="[email protected]"'); | |
console.error('export JIRA_API_TOKEN="your-api-token"'); | |
process.exit(1); | |
} | |
// Get ticket ID from command line argument | |
const ticketId = process.argv[2]; | |
if (!ticketId) { | |
console.error('Error: Please provide a ticket ID'); | |
console.error('Usage: ./jira.js TICKET-123'); | |
process.exit(1); | |
} | |
// Initialize the client and summarizer | |
const jiraClient = new JiraClient( | |
process.env.JIRA_URL, | |
process.env.JIRA_EMAIL, | |
process.env.JIRA_API_TOKEN | |
); | |
const summarizer = new JiraSummarizer(jiraClient); | |
// Get and display the ticket summary | |
async function main() { | |
try { | |
const summary = await summarizer.generateTicketSummary(ticketId); | |
console.log('\nTicket Summary:'); | |
console.log('=============='); | |
console.log(`Key: ${summary.key}`); | |
console.log(`Summary: ${summary.summary}`); | |
console.log(`Status: ${summary.status}`); | |
console.log(`Priority: ${summary.priority}`); | |
console.log(`Assignee: ${summary.assignee}`); | |
console.log(`Created: ${summary.created}`); | |
console.log(`Updated: ${summary.updated}`); | |
console.log('\nDescription:'); | |
console.log(summary.description); | |
console.log('\nRecent Comments:'); | |
if (Array.isArray(summary.comments)) { | |
summary.comments.forEach(comment => { | |
console.log(`\n${comment.author} (${comment.created}):`); | |
console.log(comment.body); | |
}); | |
} else { | |
console.log(summary.comments); | |
} | |
} catch (error) { | |
console.error('Failed to get ticket summary:', error); | |
process.exit(1); | |
} | |
} | |
main(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class JiraClient { | |
constructor(baseUrl, email, apiToken) { | |
this.baseUrl = baseUrl; | |
this.auth = Buffer.from(`${email}:${apiToken}`).toString('base64'); | |
} | |
async getTicket(ticketId) { | |
try { | |
const response = await fetch(`${this.baseUrl}/rest/api/3/issue/${ticketId}`, { | |
headers: { | |
'Authorization': `Basic ${this.auth}`, | |
'Accept': 'application/json' | |
} | |
}); | |
if (!response.ok) { | |
throw new Error(`Failed to fetch ticket: ${response.statusText}`); | |
} | |
return await response.json(); | |
} catch (error) { | |
console.error(`Error fetching ticket ${ticketId}:`, error); | |
throw error; | |
} | |
} | |
async searchTickets(jql) { | |
try { | |
const response = await fetch(`${this.baseUrl}/rest/api/3/search`, { | |
method: 'POST', | |
headers: { | |
'Authorization': `Basic ${this.auth}`, | |
'Accept': 'application/json', | |
'Content-Type': 'application/json' | |
}, | |
body: JSON.stringify({ | |
jql, | |
maxResults: 50 | |
}) | |
}); | |
if (!response.ok) { | |
throw new Error(`Failed to search tickets: ${response.statusText}`); | |
} | |
return await response.json(); | |
} catch (error) { | |
console.error('Error searching tickets:', error); | |
throw error; | |
} | |
} | |
async getProjectSummary(projectKey) { | |
try { | |
const jql = `project = ${projectKey} ORDER BY updated DESC`; | |
const tickets = await this.searchTickets(jql); | |
const summary = { | |
totalIssues: tickets.total, | |
statusBreakdown: {}, | |
priorityBreakdown: {}, | |
recentUpdates: [] | |
}; | |
tickets.issues.forEach(issue => { | |
// Count issues by status | |
const status = issue.fields.status.name; | |
summary.statusBreakdown[status] = (summary.statusBreakdown[status] || 0) + 1; | |
// Count issues by priority | |
const priority = issue.fields.priority.name; | |
summary.priorityBreakdown[priority] = (summary.priorityBreakdown[priority] || 0) + 1; | |
// Track recent updates | |
if (summary.recentUpdates.length < 5) { | |
summary.recentUpdates.push({ | |
key: issue.key, | |
summary: issue.fields.summary, | |
status, | |
updated: issue.fields.updated | |
}); | |
} | |
}); | |
return summary; | |
} catch (error) { | |
console.error(`Error getting project summary for ${projectKey}:`, error); | |
throw error; | |
} | |
} | |
} | |
class JiraSummarizer { | |
constructor(jiraClient) { | |
this.jiraClient = jiraClient; | |
} | |
async generateTicketSummary(ticketId) { | |
try { | |
const ticket = await this.jiraClient.getTicket(ticketId); | |
return { | |
key: ticket.key, | |
summary: ticket.fields.summary, | |
status: ticket.fields.status.name, | |
priority: ticket.fields.priority.name, | |
assignee: ticket.fields.assignee?.displayName || 'Unassigned', | |
created: new Date(ticket.fields.created).toLocaleDateString(), | |
updated: new Date(ticket.fields.updated).toLocaleDateString(), | |
description: this.summarizeDescription(ticket.fields.description), | |
comments: this.summarizeComments(ticket.fields.comment) | |
}; | |
} catch (error) { | |
console.error('Error generating ticket summary:', error); | |
throw error; | |
} | |
} | |
summarizeDescription(description) { | |
if (!description) return 'No description provided'; | |
// Extract main points from description | |
// This is a simple implementation - could be enhanced with NLP | |
const sentences = description.split(/[.!?]+/); | |
return sentences.slice(0, 3).join('. ') + (sentences.length > 3 ? '...' : ''); | |
} | |
summarizeComments(comments) { | |
if (!comments?.comments?.length) return 'No comments'; | |
return comments.comments.slice(-3).map(comment => ({ | |
author: comment.author.displayName, | |
created: new Date(comment.created).toLocaleDateString(), | |
body: this.summarizeDescription(comment.body) | |
})); | |
} | |
async generateProjectStatus(projectKey) { | |
try { | |
const summary = await this.jiraClient.getProjectSummary(projectKey); | |
return { | |
overview: `Project ${projectKey} has ${summary.totalIssues} total issues`, | |
status: this.formatBreakdown(summary.statusBreakdown), | |
priority: this.formatBreakdown(summary.priorityBreakdown), | |
recentActivity: summary.recentUpdates.map(update => | |
`${update.key}: ${update.summary} (${update.status})` | |
).join('\n') | |
}; | |
} catch (error) { | |
console.error('Error generating project status:', error); | |
throw error; | |
} | |
} | |
formatBreakdown(breakdown) { | |
return Object.entries(breakdown) | |
.map(([key, value]) => `${key}: ${value}`) | |
.join(', '); | |
} | |
} | |
// Example usage: | |
const jiraClient = new JiraClient( | |
'https://your-domain.atlassian.net', | |
'[email protected]', | |
'your-api-token' | |
); | |
const summarizer = new JiraSummarizer(jiraClient); | |
// Get ticket summary | |
async function getTicketSummary(ticketId) { | |
try { | |
const summary = await summarizer.generateTicketSummary(ticketId); | |
console.log('Ticket Summary:', JSON.stringify(summary, null, 2)); | |
} catch (error) { | |
console.error('Failed to get ticket summary:', error); | |
} | |
} | |
// Get project status | |
async function getProjectStatus(projectKey) { | |
try { | |
const status = await summarizer.generateProjectStatus(projectKey); | |
console.log('Project Status:', JSON.stringify(status, null, 2)); | |
} catch (error) { | |
console.error('Failed to get project status:', error); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment