Skip to content

Commit

Permalink
🔨 Add dev helper to get Jira issue description in markdown format.
Browse files Browse the repository at this point in the history
  • Loading branch information
klondikemarlen committed Oct 2, 2024
1 parent 1b5cffe commit 3ddef69
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 4 deletions.
13 changes: 13 additions & 0 deletions bin/dev
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class DevHelper
COMMAND_TO_METHOD = {
"ts-node" => :ts_node,
"branch-from" => :branch_from,
"description-from" => :description_from,
}
METHOD_TO_COMMAND = COMMAND_TO_METHOD.invert

Expand Down Expand Up @@ -189,6 +190,18 @@ class DevHelper
system("git checkout -b #{branch_name}")
end

##
# Fetches the description of a Jira issue and prints it to the console in markdown format.
# Example:
# dev description-from https://yg-hpw.atlassian.net/browse/ELCC-61
#
# Produces:
# ... a bunch of markdown text ...
def description_from(jira_issue_url, *args, **kwargs)
description = JiraApi.fetch_description_as_markdown(jira_issue_url)
puts description
end

def bash_completions
completions = public_methods(false).reject { |word|
%i[call].include?(word)
Expand Down
34 changes: 30 additions & 4 deletions bin/jira_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
require 'json'
require 'uri'

require_relative "./prose_mirror_to_json.rb"

class JiraApi
JIRA_USERNAME = ENV['JIRA_USERNAME']
JIRA_API_TOKEN = ENV['JIRA_API_TOKEN']
Expand All @@ -14,17 +16,30 @@ def self.build_branch_name(jira_ticket_url)
end

issue_key = extract_issue_key(jira_ticket_url)
issue_title = fetch_issue_title(issue_key)
issue_data = fetch_issue_data(issue_key)
issue_title = extract_issue_title(issue_data)
format_branch_name(issue_key, issue_title)
end

def self.fetch_description_as_markdown(jira_ticket_url)
if JIRA_USERNAME.nil? || JIRA_API_TOKEN.nil?
puts 'Please set JIRA_USERNAME and JIRA_API_TOKEN environment variables'
return
end

issue_key = extract_issue_key(jira_ticket_url)
issue_data = fetch_issue_data(issue_key)
issue_description = extract_issue_description(issue_data)
prose_mirror_to_markdown(issue_description)
end

private

def self.extract_issue_key(url)
url.match(%r{/browse/([A-Z]+-\d+)})[1]
end

def self.fetch_issue_title(issue_key)
def self.fetch_issue_data(issue_key)
uri = URI("#{JIRA_SITE}/rest/api/3/issue/#{issue_key}")
request = Net::HTTP::Get.new(uri)
request.basic_auth(JIRA_USERNAME, JIRA_API_TOKEN)
Expand All @@ -33,12 +48,23 @@ def self.fetch_issue_title(issue_key)
http.request(request)
end

data = JSON.parse(response.body)
data['fields']['summary']
JSON.parse(response.body)
end

def self.extract_issue_title(issue_data)
issue_data['fields']['summary']
end

def self.extract_issue_description(issue_data)
issue_data['fields']['description']
end

def self.format_branch_name(issue_key, issue_title)
formatted_title = issue_title.downcase.gsub(/\s+/, '-').gsub(/[^a-z0-9\-]/, '')
"#{issue_key.downcase}/#{formatted_title}"
end

def self.prose_mirror_to_markdown(description)
ProseMirrorToMarkdown.call(description)
end
end
128 changes: 128 additions & 0 deletions bin/prose_mirror_to_json.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
require 'json'

##
# Example usage:
#
# Suppose 'prosemirror_json' is the JSON data you've provided.
#
# Load the JSON data
# prosemirror_json = DownloadJiraDescription.call(jira_ticket_url)
#
# markdown = ProseMirrorToMarkdown.call(prosemirror_json)
# puts markdown
class ProseMirrorToMarkdown
def self.call(prosemirror_json)
new(prosemirror_json).to_markdown
end

def initialize(prosemirror_json)
@doc = prosemirror_json
end

def to_markdown
process_nodes(@doc['content']).join("\n\n")
end

private

def process_nodes(nodes)
nodes.flat_map { |node| process_node(node) }
end

def process_node(node)
case node['type']
when 'doc'
process_nodes(node['content'])
when 'paragraph'
process_paragraph(node)
when 'heading'
process_heading(node)
when 'blockquote'
process_blockquote(node)
when 'orderedList'
process_ordered_list(node)
when 'bulletList'
process_bullet_list(node)
when 'listItem'
process_list_item(node)
when 'text'
process_text(node)
else
# For any other nodes, process their content if they have any
if node['content']
process_nodes(node['content'])
else
[]
end
end
end

def process_paragraph(node)
[process_inline_content(node['content'])]
end

def process_heading(node)
level = node['attrs']['level']
content = process_inline_content(node['content'])
["#{'#' * level} #{content}"]
end

def process_blockquote(node)
content = process_nodes(node['content']).map { |line| "> #{line}" }.join("\n")
[content]
end

def process_ordered_list(node)
start_number = node['attrs']['order'] || 1
items = node['content']
process_list_items(items, ordered: true, start_number: start_number)
end

def process_bullet_list(node)
items = node['content']
process_list_items(items, ordered: false)
end

def process_list_items(items, ordered:, start_number: 1)
counter = start_number
items.flat_map do |item|
lines = process_nodes(item['content'])
prefix = ordered ? "#{counter}. " : "- "
counter += 1
lines.map.with_index do |line, idx|
if idx == 0
"#{prefix}#{line}"
else
" #{line}"
end
end
end
end

def process_text(node)
text = node['text']
if node['marks']
node['marks'].each do |mark|
case mark['type']
when 'bold'
text = "**#{text}**"
when 'italic'
text = "*#{text}*"
when 'code'
text = "`#{text}`"
when 'link'
href = mark['attrs']['href']
text = "[#{text}](#{href})"
when 'strike'
text = "~~#{text}~~"
# Add more mark types as needed
end
end
end
text
end

def process_inline_content(content)
content.map { |node| process_node(node) }.join
end
end

0 comments on commit 3ddef69

Please sign in to comment.