linked_issue.rb 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class GitHub
  3. class LinkedIssue
  4. STATES_MAPPING = {
  5. 'OPEN' => 'open',
  6. 'CLOSED' => 'closed'
  7. }.freeze
  8. QUERY = <<-GRAPHQL.freeze
  9. query($repositor_owner: String!, $repository_name: String!, $issue_id: Int!) {
  10. repository(owner: $repositor_owner, name: $repository_name) {
  11. issue(number: $issue_id) {
  12. number
  13. title
  14. state
  15. url
  16. milestone {
  17. title
  18. }
  19. assignees(last: 100) {
  20. edges {
  21. node {
  22. name
  23. }
  24. }
  25. }
  26. labels(last: 100) {
  27. edges {
  28. node {
  29. name
  30. color
  31. }
  32. }
  33. }
  34. }
  35. }
  36. }
  37. GRAPHQL
  38. attr_reader :client
  39. def initialize(client)
  40. @client = client
  41. end
  42. def find_by(url)
  43. @result = query_by_url(url)
  44. return if @result.blank?
  45. to_h
  46. end
  47. private
  48. def to_h
  49. {
  50. id: @result['number'].to_s,
  51. title: @result['title'],
  52. url: @result['url'],
  53. icon_state: STATES_MAPPING.fetch(@result['state'], @result['state']),
  54. milestone: milestone,
  55. assignees: assignees,
  56. labels: labels,
  57. }
  58. end
  59. def assignees
  60. @result['assignees']['edges'].map do |assignee|
  61. assignee['node']['name']
  62. end
  63. end
  64. def labels
  65. @result['labels']['edges'].map do |label|
  66. {
  67. text_color: text_color(label['node']['color']),
  68. color: "##{label['node']['color']}",
  69. title: label['node']['name']
  70. }
  71. end
  72. end
  73. def text_color(background_color)
  74. background_color.to_i(16) > 0xFFF / 2 ? '#000000' : '#FFFFFF'
  75. end
  76. def milestone
  77. @result.dig('milestone', 'title')
  78. end
  79. def query_by_url(url)
  80. response = client.perform(
  81. query: GitHub::LinkedIssue::QUERY,
  82. variables: variables!(url)
  83. )
  84. response.dig('data', 'repository', 'issue')
  85. end
  86. def variables!(url)
  87. if url !~ %r{^https?://([^/]+)/([^/]+)/([^/]+)/issues/(\d+)$}
  88. raise Exceptions::UnprocessableEntity, __('Invalid GitHub issue link format')
  89. end
  90. host = $1
  91. repositor_owner = $2
  92. repository_name = $3
  93. id = $4
  94. if client.endpoint.exclude?(host)
  95. raise Exceptions::UnprocessableEntity, "Issue link doesn't match configured GitHub endpoint '#{client.endpoint}'"
  96. end
  97. {
  98. repositor_owner: repositor_owner,
  99. repository_name: repository_name,
  100. issue_id: id.to_i,
  101. }
  102. end
  103. end
  104. end