linked_issue.rb 2.7 KB

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