1# frozen_string_literal: true
2
3module Ci
4  ##
5  # This domain model is a representation of a group of jobs that are related
6  # to each other, like `rspec 0 1`, `rspec 0 2`.
7  #
8  # It is not persisted in the database.
9  #
10  class Group
11    include StaticModel
12    include Gitlab::Utils::StrongMemoize
13    include GlobalID::Identification
14
15    attr_reader :project, :stage, :name, :jobs
16
17    delegate :size, to: :jobs
18
19    def initialize(project, stage, name:, jobs:)
20      @project = project
21      @stage = stage
22      @name = name
23      @jobs = jobs
24    end
25
26    def id
27      "#{stage.id}-#{name}"
28    end
29
30    def ==(other)
31      other.present? && other.is_a?(self.class) &&
32        project == other.project &&
33        stage == other.stage &&
34        name == other.name
35    end
36
37    def status
38      strong_memoize(:status) do
39        status_struct.status
40      end
41    end
42
43    def success?
44      status.to_s == 'success'
45    end
46
47    def has_warnings?
48      status_struct.warnings?
49    end
50
51    def status_struct
52      strong_memoize(:status_struct) do
53        Gitlab::Ci::Status::Composite
54          .new(@jobs, project: project)
55      end
56    end
57
58    def detailed_status(current_user)
59      if jobs.one?
60        jobs.first.detailed_status(current_user)
61      else
62        Gitlab::Ci::Status::Group::Factory
63          .new(self, current_user).fabricate!
64      end
65    end
66
67    # Construct a grouping of statuses for this stage.
68    # We allow the caller to pass in statuses for efficiency (avoiding N+1
69    # queries).
70    def self.fabricate(project, stage, statuses = nil)
71      statuses ||= stage.latest_statuses
72
73      statuses
74        .sort_by(&:sortable_name).group_by(&:group_name)
75        .map do |group_name, grouped_statuses|
76          self.new(project, stage, name: group_name, jobs: grouped_statuses)
77        end
78    end
79  end
80end
81