Welcome to mirror list, hosted at ThFree Co, Russian Federation.

records_fetcher.rb « cycle_analytics « analytics « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 2662aa38d6b4448ca081175edd20786033bc445c (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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# frozen_string_literal: true

module Gitlab
  module Analytics
    module CycleAnalytics
      class RecordsFetcher
        include Gitlab::Utils::StrongMemoize
        include StageQueryHelpers
        include Gitlab::CycleAnalytics::MetricsTables

        MAX_RECORDS = 20

        MAPPINGS = {
          Issue => {
            finder_class: IssuesFinder,
            serializer_class: AnalyticsIssueSerializer,
            includes_for_query: { project: [:namespace], author: [] },
            columns_for_select: %I[title iid id created_at author_id project_id]
          },
          MergeRequest => {
            finder_class: MergeRequestsFinder,
            serializer_class: AnalyticsMergeRequestSerializer,
            includes_for_query: { target_project: [:namespace], author: [] },
            columns_for_select: %I[title iid id created_at author_id state target_project_id]
          }
        }.freeze

        delegate :subject_class, to: :stage

        def initialize(stage:, query:, params: {})
          @stage = stage
          @query = query
          @params = params
        end

        def serialized_records
          strong_memoize(:serialized_records) do
            # special case (legacy): 'Test' and 'Staging' stages should show Ci::Build records
            if default_test_stage? || default_staging_stage?
              AnalyticsBuildSerializer.new.represent(ci_build_records.map { |e| e['build'] })
            else
              records.map do |record|
                project = record.project
                attributes = record.attributes.merge({
                  project_path: project.path,
                  namespace_path: project.namespace.path,
                  author: record.author
                })
                serializer.represent(attributes)
              end
            end
          end
        end

        private

        attr_reader :stage, :query, :params

        def finder_query
          MAPPINGS
            .fetch(subject_class)
            .fetch(:finder_class)
            .new(params.fetch(:current_user), finder_params.fetch(stage.parent.class))
            .execute
        end

        def columns
          MAPPINGS.fetch(subject_class).fetch(:columns_for_select).map do |column_name|
            subject_class.arel_table[column_name]
          end
        end

        # EE will override this to include Group rules
        def finder_params
          {
            Project => { project_id: stage.parent_id }
          }
        end

        def default_test_stage?
          stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_test_stage)
        end

        def default_staging_stage?
          stage.matches_with_stage_params?(Gitlab::Analytics::CycleAnalytics::DefaultStages.params_for_staging_stage)
        end

        def serializer
          MAPPINGS.fetch(subject_class).fetch(:serializer_class).new
        end

        # Loading Ci::Build records instead of MergeRequest records
        # rubocop: disable CodeReuse/ActiveRecord
        def ci_build_records
          ci_build_join = mr_metrics_table
            .join(build_table)
            .on(mr_metrics_table[:pipeline_id].eq(build_table[:commit_id]))
            .join_sources

          q = ordered_and_limited_query
            .joins(ci_build_join)
            .select(build_table[:id], round_duration_to_seconds.as('total_time'))

          results = execute_query(q).to_a

          Gitlab::CycleAnalytics::Updater.update!(results, from: 'id', to: 'build', klass: ::Ci::Build.includes({ project: [:namespace], user: [], pipeline: [] }))
        end

        def ordered_and_limited_query
          query
            .reorder(stage.end_event.timestamp_projection.desc)
            .limit(MAX_RECORDS)
        end

        def records
          results = finder_query
            .merge(ordered_and_limited_query)
            .select(*columns, round_duration_to_seconds.as('total_time'))

          # using preloader instead of includes to avoid AR generating a large column list
          ActiveRecord::Associations::Preloader.new.preload(
            results,
            MAPPINGS.fetch(subject_class).fetch(:includes_for_query)
          )

          results
        end
        # rubocop: enable CodeReuse/ActiveRecord
      end
    end
  end
end

Gitlab::Analytics::CycleAnalytics::RecordsFetcher.prepend_if_ee('EE::Gitlab::Analytics::CycleAnalytics::RecordsFetcher')