summaryrefslogtreecommitdiff
path: root/lib/gitlab/danger/teammate.rb
blob: 651b002d2bf92773d6ac67740ebd8271a1a684ac (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
61
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
# frozen_string_literal: true

require 'cgi'
require 'set'

module Gitlab
  module Danger
    class Teammate
      attr_reader :name, :username, :role, :projects

      AT_CAPACITY_EMOJI = Set.new(%w[red_circle]).freeze
      OOO_EMOJI = Set.new(%w[
        palm_tree
        beach beach_umbrella beach_with_umbrella
      ]).freeze

      def initialize(options = {})
        @username = options['username']
        @name = options['name'] || @username
        @role = options['role']
        @projects = options['projects']
      end

      def markdown_name
        "[#{name}](https://gitlab.com/#{username}) (`@#{username}`)"
      end

      def in_project?(name)
        projects&.has_key?(name)
      end

      # Traintainers also count as reviewers
      def reviewer?(project, category, labels)
        has_capability?(project, category, :reviewer, labels) ||
          traintainer?(project, category, labels)
      end

      def traintainer?(project, category, labels)
        has_capability?(project, category, :trainee_maintainer, labels)
      end

      def maintainer?(project, category, labels)
        has_capability?(project, category, :maintainer, labels)
      end

      def status
        return @status if defined?(@status)

        @status ||=
          begin
            Gitlab::Danger::RequestHelper.http_get_json(status_api_endpoint)
          rescue Gitlab::Danger::RequestHelper::HTTPError, JSON::ParserError
            nil # better no status than a crashing Danger
          end
      end

      # @return [Boolean]
      def available?
        !out_of_office? && has_capacity?
      end

      private

      def status_api_endpoint
        "https://gitlab.com/api/v4/users/#{CGI.escape(username)}/status"
      end

      def status_emoji
        status&.dig("emoji")
      end

      # @return [Boolean]
      def out_of_office?
        status&.dig("message")&.match?(/OOO/i) || OOO_EMOJI.include?(status_emoji)
      end

      # @return [Boolean]
      def has_capacity?
        !AT_CAPACITY_EMOJI.include?(status_emoji)
      end

      def has_capability?(project, category, kind, labels)
        case category
        when :test
          area = role[/Software Engineer in Test(?:.*?, (\w+))/, 1]

          area && labels.any?("devops::#{area.downcase}") if kind == :reviewer
        when :engineering_productivity
          return false unless role[/Engineering Productivity/]
          return true if kind == :reviewer

          capabilities(project).include?("#{kind} backend")
        else
          capabilities(project).include?("#{kind} #{category}")
        end
      end

      def capabilities(project)
        Array(projects.fetch(project, []))
      end
    end
  end
end