1# frozen_string_literal: true
2
3# Provides an action which fetches a metrics dashboard according
4# to the parameters specified by the controller.
5module MetricsDashboard
6  include RenderServiceResults
7  include ChecksCollaboration
8  include EnvironmentsHelper
9
10  extend ActiveSupport::Concern
11
12  def metrics_dashboard
13    result = dashboard_finder.find(
14      project_for_dashboard,
15      current_user,
16      decoded_params
17    )
18
19    if result
20      result[:all_dashboards] = all_dashboards if include_all_dashboards?
21      result[:metrics_data] = metrics_data(project_for_dashboard, environment_for_dashboard)
22    end
23
24    respond_to do |format|
25      if result.nil?
26        format.json { continue_polling_response }
27      elsif result[:status] == :success
28        format.json { render dashboard_success_response(result) }
29      else
30        format.json { render dashboard_error_response(result) }
31      end
32    end
33  end
34
35  private
36
37  def all_dashboards
38    dashboard_finder
39      .find_all_paths(project_for_dashboard)
40      .map(&method(:amend_dashboard))
41  end
42
43  def amend_dashboard(dashboard)
44    project_dashboard = project_for_dashboard && !dashboard[:out_of_the_box_dashboard]
45
46    dashboard[:can_edit] = project_dashboard ? can_edit?(dashboard) : false
47    dashboard[:project_blob_path] = project_dashboard ? dashboard_project_blob_path(dashboard) : nil
48    dashboard[:starred] = starred_dashboards.include?(dashboard[:path])
49    dashboard[:user_starred_path] = project_for_dashboard ? user_starred_path(project_for_dashboard, dashboard[:path]) : nil
50
51    dashboard
52  end
53
54  def user_starred_path(project, path)
55    expose_path(api_v4_projects_metrics_user_starred_dashboards_path(id: project.id, params: { dashboard_path: path }))
56  end
57
58  def dashboard_project_blob_path(dashboard)
59    project_blob_path(project_for_dashboard, File.join(project_for_dashboard.default_branch, dashboard.fetch(:path, "")))
60  end
61
62  def can_edit?(dashboard)
63    can_collaborate_with_project?(project_for_dashboard, ref: project_for_dashboard.default_branch)
64  end
65
66  # Override in class to provide arguments to the finder.
67  def metrics_dashboard_params
68    {}
69  end
70
71  # Override in class if response requires complete list of
72  # dashboards in addition to requested dashboard body.
73  def include_all_dashboards?
74    false
75  end
76
77  def dashboard_finder
78    ::Gitlab::Metrics::Dashboard::Finder
79  end
80
81  def starred_dashboards
82    @starred_dashboards ||= begin
83      if project_for_dashboard.present?
84        ::Metrics::UsersStarredDashboardsFinder
85          .new(user: current_user, project: project_for_dashboard)
86          .execute
87          .map(&:dashboard_path)
88          .to_set
89      else
90        Set.new
91      end
92    end
93  end
94
95  # Project is not defined for group and admin level clusters.
96  def project_for_dashboard
97    defined?(project) ? project : nil
98  end
99
100  def environment_for_dashboard
101    defined?(environment) ? environment : nil
102  end
103
104  def dashboard_success_response(result)
105    {
106      status: :ok,
107      json: result.slice(:all_dashboards, :dashboard, :status, :metrics_data)
108    }
109  end
110
111  def dashboard_error_response(result)
112    {
113      status: result[:http_status] || :bad_request,
114      json: result.slice(:all_dashboards, :message, :status)
115    }
116  end
117
118  def decoded_params
119    params = metrics_dashboard_params
120
121    if params[:dashboard_path]
122      params[:dashboard_path] = CGI.unescape(params[:dashboard_path])
123    end
124
125    params
126  end
127end
128