Class: N2B::JiraClient
- Inherits:
-
Object
- Object
- N2B::JiraClient
- Defined in:
- lib/n2b/jira_client.rb
Defined Under Namespace
Classes: JiraApiError
Instance Method Summary collapse
- #extract_requirements_from_description(description_string) ⇒ Object
- #fetch_ticket(ticket_key_or_url) ⇒ Object
-
#initialize(config) ⇒ JiraClient
constructor
A new instance of JiraClient.
-
#test_connection ⇒ Object
Add test connection functionality.
- #update_ticket(ticket_key_or_url, comment) ⇒ Object
Constructor Details
#initialize(config) ⇒ JiraClient
Returns a new instance of JiraClient.
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/n2b/jira_client.rb', line 12 def initialize(config) @config = config @jira_config = @config['jira'] || {} # Ensure jira key exists unless @jira_config['domain'] && @jira_config['email'] && @jira_config['api_key'] raise ArgumentError, "Jira domain, email, and API key must be configured in N2B settings." end # Handle domain that may or may not include protocol domain = @jira_config['domain'].to_s.strip if domain.start_with?('http://') || domain.start_with?('https://') # Domain already includes protocol @base_url = "#{domain.chomp('/')}/rest/api/3" else # Domain doesn't include protocol, add https:// @base_url = "https://#{domain.chomp('/')}/rest/api/3" end end |
Instance Method Details
#extract_requirements_from_description(description_string) ⇒ Object
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/n2b/jira_client.rb', line 140 def extract_requirements_from_description(description_string) extracted_lines = [] in_requirements_section = false # Headers that trigger requirement extraction. Case-insensitive. # Jira often uses h1, h2, etc. for headers, or bold text. # We'll look for lines that *start* with these, possibly after Jira's header markup like "hN. " # Or common text like "Acceptance Criteria:", "Requirements:" # Also include comment-specific implementation keywords requirement_headers_regex = /^(h[1-6]\.\s*)?(Requirements|Acceptance Criteria|Tasks|Key Deliverables|Scope|User Stories|Implementation|Testing|Technical|Additional|Clarification|Comment \d+)/i # Regex to identify common list item markers _list_item_regex = /^\s*[\*\-\+]\s+/ # Unused but kept for potential future use # Regex for lines that look like section headers (to stop capturing) # This is a simple heuristic: a line with a few words, ending with a colon, or Jira hN. style section_break_regex = /^(h[1-6]\.\s*)?\w+(\s+\w+){0,3}:?\s*$/i description_string.to_s.each_line do |line| # Handle nil description_string stripped_line = line.strip if stripped_line.match?(requirement_headers_regex) in_requirements_section = true # Add the header itself to the extracted content if desired, or just use it as a trigger # For now, let's add the line to give context. extracted_lines << stripped_line next # Move to the next line end if in_requirements_section # If we encounter another significant header, stop capturing this section # (unless it's another requirements header, which is fine) if stripped_line.match?(section_break_regex) && !stripped_line.match?(requirement_headers_regex) # Check if this new header is one of the requirement types. If so, continue. # Otherwise, break. This logic is simplified: if it's any other header, stop. is_another_req_header = false # Placeholder for more complex logic if needed requirement_headers_regex.match(stripped_line) { is_another_req_header = true } unless is_another_req_header in_requirements_section = false # Stop capturing # Potentially add a separator if concatenating multiple distinct sections later # extracted_lines << "---" next # Don't include this new non-req header in current section else # It's another requirement-related header, so add it and continue extracted_lines << stripped_line next end end # Capture list items or general text within the section # For now, we are quite inclusive of lines within a detected section. # We could be more strict and only take list_item_regex lines, # but often text paragraphs under a heading are relevant too. extracted_lines << stripped_line unless stripped_line.empty? end end if extracted_lines.empty? # Fallback: return the entire description if no specific sections found return description_string.to_s.strip # Handle nil and strip else # Join extracted lines and clean up excessive newlines # Replace 3+ newlines with 2, and 2+ newlines with 2 (effectively max 2 newlines) # Also, strip leading/trailing whitespace from the final result. return extracted_lines.join("\n").gsub(/\n{3,}/, "\n\n").gsub(/\n{2,}/, "\n\n").strip end end |
#fetch_ticket(ticket_key_or_url) ⇒ Object
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/n2b/jira_client.rb', line 30 def fetch_ticket(ticket_key_or_url) domain, ticket_key = parse_ticket_input(ticket_key_or_url) unless ticket_key raise JiraApiError, "Could not extract ticket key from '#{ticket_key_or_url}'." end puts "Fetching Jira ticket: #{ticket_key} from domain: #{domain || @jira_config['domain']}" puts "Fetching ticket comments for additional context..." begin # Fetch ticket details ticket_path = "/rest/api/3/issue/#{ticket_key}" ticket_data = make_api_request('GET', ticket_path) # Fetch ticket comments comments_path = "/rest/api/3/issue/#{ticket_key}/comment" comments_response = make_api_request('GET', comments_path) comments_data = comments_response['comments'] || [] puts "Successfully fetched ticket and #{comments_data.length} comments" # Process real data return process_ticket_data(ticket_data, comments_data) rescue JiraApiError => e puts "⚠️ Failed to fetch from Jira API: #{e.}" puts "Falling back to dummy data for development..." return fetch_dummy_ticket_data(ticket_key) end end |
#test_connection ⇒ Object
Add test connection functionality
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/n2b/jira_client.rb', line 113 def test_connection puts "🧪 Testing Jira API connection..." begin # Test 1: Basic authentication response = make_api_request('GET', '/myself') puts "✅ Authentication successful" puts " Account: #{response['displayName']} (#{response['emailAddress']})" # Test 2: Project access projects = make_api_request('GET', '/project') puts "✅ Can access #{projects.length} projects" # Test 3: Comment permissions (try to get comments from any issue) if projects.any? project_key = projects.first['key'] puts "✅ Basic permissions verified for project: #{project_key}" end puts "🎉 Jira connection test successful!" true rescue => e puts "❌ Jira connection test failed: #{e.}" false end end |
#update_ticket(ticket_key_or_url, comment) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/n2b/jira_client.rb', line 62 def update_ticket(ticket_key_or_url, comment) _domain, ticket_key = parse_ticket_input(ticket_key_or_url) # Use _domain to indicate it's not used here unless ticket_key raise JiraApiError, "Could not extract ticket key from '#{ticket_key_or_url}' for update." end puts "Updating Jira ticket #{ticket_key} with analysis comment..." # Generate comment using template system template_comment = generate_templated_comment(comment) if debug_mode? puts "🔍 DEBUG: Generated template comment (#{template_comment.length} chars):" puts "--- TEMPLATE COMMENT START ---" puts template_comment puts "--- TEMPLATE COMMENT END ---" end # Prepare the comment body in Jira's Atlassian Document Format (ADF) comment_body = { "body" => format_comment_as_adf(template_comment) } if debug_mode? puts "🔍 DEBUG: Formatted ADF comment body:" puts "--- ADF BODY START ---" puts JSON.pretty_generate(comment_body) puts "--- ADF BODY END ---" end # Make the API call to add a comment path = "/rest/api/3/issue/#{ticket_key}/comment" puts "🔍 DEBUG: Making API request to: #{path}" if debug_mode? _response = make_api_request('POST', path, comment_body) puts "✅ Successfully added comment to Jira ticket #{ticket_key}" true rescue JiraApiError => e puts "❌ Failed to update Jira ticket #{ticket_key}: #{e.}" if debug_mode? puts "🔍 DEBUG: Full error details:" puts " - Ticket key: #{ticket_key}" puts " - Template comment length: #{template_comment&.length || 'nil'}" puts " - Comment body keys: #{comment_body&.keys || 'nil'}" end false end |