Commit bead8e6a authored by mfluharty's avatar mfluharty

Move codeclimate functions out of ee directory

MR widget entity should expose codeclimate and blob paths
(for base pipeline and head pipeline, four paths total)
which also requires the report artifacts from the pipeline
which also requires associated_file_types_for from the artifact
parent 35379493
...@@ -245,6 +245,12 @@ module Ci ...@@ -245,6 +245,12 @@ module Ci
self.update_column(:file_store, file.object_store) self.update_column(:file_store, file.object_store)
end end
def self.associated_file_types_for(file_type)
return unless file_types.include?(file_type)
[file_type]
end
def self.total_size def self.total_size
self.sum(:size) self.sum(:size)
end end
......
...@@ -299,6 +299,12 @@ module Ci ...@@ -299,6 +299,12 @@ module Ci
project: [merge_request.source_project, merge_request.target_project]) project: [merge_request.source_project, merge_request.target_project])
end end
# This structure describes feature levels
# to access the file types for given reports
REPORT_LICENSED_FEATURES = {
codequality: nil
}.freeze
# Returns the pipelines in descending order (= newest first), optionally # Returns the pipelines in descending order (= newest first), optionally
# limited to a number of references. # limited to a number of references.
# #
...@@ -624,6 +630,45 @@ module Ci ...@@ -624,6 +630,45 @@ module Ci
end end
end end
def batch_lookup_report_artifact_for_file_type(file_type)
return unless available_licensed_report_type?(file_type)
latest_report_artifacts
.values_at(*::Ci::JobArtifact.associated_file_types_for(file_type.to_s))
.flatten
.compact
.last
end
# This batch loads the latest reports for each CI job artifact
# type (e.g. sast, dast, etc.) in a single SQL query to eliminate
# the need to do N different `job_artifacts.where(file_type:
# X).last` calls.
#
# Return a hash of file type => array of 1 job artifact
def latest_report_artifacts
::Gitlab::SafeRequestStore.fetch("pipeline:#{self.id}:latest_report_artifacts") do
# Note we use read_attribute(:project_id) to read the project
# ID instead of self.project_id. The latter appears to load
# the Project model. This extra filter doesn't appear to
# affect query plan but included to ensure we don't leak the
# wrong informaiton.
::Ci::JobArtifact.where(
id: job_artifacts.with_reports
.select('max(ci_job_artifacts.id) as id')
.where(project_id: self.read_attribute(:project_id))
.group(:file_type)
)
.preload(:job)
.group_by(&:file_type)
end
end
def available_licensed_report_type?(file_type)
feature_names = REPORT_LICENSED_FEATURES.fetch(file_type)
feature_names.nil? || feature_names.any? { |feature| project.feature_available?(feature) }
end
def has_kubernetes_active? def has_kubernetes_active?
project.deployment_platform&.active? project.deployment_platform&.active?
end end
......
...@@ -110,6 +110,17 @@ module Ci ...@@ -110,6 +110,17 @@ module Ci
merge_request_presenter&.target_branch_link merge_request_presenter&.target_branch_link
end end
def downloadable_path_for_report_type(file_type)
if (job_artifact = batch_lookup_report_artifact_for_file_type(file_type)) &&
can?(current_user, :read_build, job_artifact.job)
download_project_job_artifacts_path(
job_artifact.project,
job_artifact.job,
file_type: file_type,
proxy: true)
end
end
private private
def plain_ref_name def plain_ref_name
......
...@@ -85,6 +85,26 @@ class MergeRequestWidgetEntity < Grape::Entity ...@@ -85,6 +85,26 @@ class MergeRequestWidgetEntity < Grape::Entity
end end
end end
expose :blob_path do
expose :head_path, if: -> (mr, _) { mr.head_pipeline_sha } do |merge_request|
project_blob_path(merge_request.project, merge_request.head_pipeline_sha)
end
expose :base_path, if: -> (mr, _) { mr.base_pipeline_sha } do |merge_request|
project_blob_path(merge_request.project, merge_request.base_pipeline_sha)
end
end
expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:codequality) } do
expose :head_path do |merge_request|
head_pipeline_downloadable_path_for_report_type(:codequality)
end
expose :base_path do |merge_request|
base_pipeline_downloadable_path_for_report_type(:codequality)
end
end
private private
delegate :current_user, to: :request delegate :current_user, to: :request
...@@ -103,6 +123,16 @@ class MergeRequestWidgetEntity < Grape::Entity ...@@ -103,6 +123,16 @@ class MergeRequestWidgetEntity < Grape::Entity
can?(current_user, :read_build, merge_request.source_project) && can?(current_user, :read_build, merge_request.source_project) &&
can?(current_user, :create_pipeline, merge_request.source_project) can?(current_user, :create_pipeline, merge_request.source_project)
end end
def head_pipeline_downloadable_path_for_report_type(file_type)
object.head_pipeline&.present(current_user: current_user)
&.downloadable_path_for_report_type(file_type)
end
def base_pipeline_downloadable_path_for_report_type(file_type)
object.base_pipeline&.present(current_user: current_user)
&.downloadable_path_for_report_type(file_type)
end
end end
MergeRequestWidgetEntity.prepend_if_ee('EE::MergeRequestWidgetEntity') MergeRequestWidgetEntity.prepend_if_ee('EE::MergeRequestWidgetEntity')
...@@ -91,16 +91,6 @@ module EE ...@@ -91,16 +91,6 @@ module EE
!merge_train_pipeline? && super !merge_train_pipeline? && super
end end
def batch_lookup_report_artifact_for_file_type(file_type)
return unless available_licensed_report_type?(file_type)
latest_report_artifacts
.values_at(*::Ci::JobArtifact.associated_file_types_for(file_type.to_s))
.flatten
.compact
.last
end
def expose_license_scanning_data? def expose_license_scanning_data?
batch_lookup_report_artifact_for_file_type(:license_scanning).present? batch_lookup_report_artifact_for_file_type(:license_scanning).present?
end end
...@@ -173,35 +163,6 @@ module EE ...@@ -173,35 +163,6 @@ module EE
def merge_train_ref? def merge_train_ref?
::MergeRequest.merge_train_ref?(ref) ::MergeRequest.merge_train_ref?(ref)
end end
# This batch loads the latest reports for each CI job artifact
# type (e.g. sast, dast, etc.) in a single SQL query to eliminate
# the need to do N different `job_artifacts.where(file_type:
# X).last` calls.
#
# Return a hash of file type => array of 1 job artifact
def latest_report_artifacts
::Gitlab::SafeRequestStore.fetch("pipeline:#{self.id}:latest_report_artifacts") do
# Note we use read_attribute(:project_id) to read the project
# ID instead of self.project_id. The latter appears to load
# the Project model. This extra filter doesn't appear to
# affect query plan but included to ensure we don't leak the
# wrong informaiton.
::Ci::JobArtifact.where(
id: job_artifacts.with_reports
.select('max(ci_job_artifacts.id) as id')
.where(project_id: self.read_attribute(:project_id))
.group(:file_type)
)
.preload(:job)
.group_by(&:file_type)
end
end
def available_licensed_report_type?(file_type)
feature_names = REPORT_LICENSED_FEATURES.fetch(file_type)
feature_names.nil? || feature_names.any? { |feature| project.feature_available?(feature) }
end
end end
end end
end end
...@@ -28,17 +28,6 @@ module EE ...@@ -28,17 +28,6 @@ module EE
batch_lookup_report_artifact_for_file_type(:container_scanning) batch_lookup_report_artifact_for_file_type(:container_scanning)
end end
def downloadable_path_for_report_type(file_type)
if (job_artifact = batch_lookup_report_artifact_for_file_type(file_type)) &&
can?(current_user, :read_build, job_artifact.job)
download_project_job_artifacts_path(
job_artifact.project,
job_artifact.job,
file_type: job_artifact.file_type,
proxy: true)
end
end
def degradation_threshold(file_type) def degradation_threshold(file_type)
if (job_artifact = batch_lookup_report_artifact_for_file_type(file_type)) && if (job_artifact = batch_lookup_report_artifact_for_file_type(file_type)) &&
can?(current_user, :read_build, job_artifact.job) can?(current_user, :read_build, job_artifact.job)
......
...@@ -6,26 +6,6 @@ module EE ...@@ -6,26 +6,6 @@ module EE
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
expose :blob_path do
expose :head_path, if: -> (mr, _) { mr.head_pipeline_sha } do |merge_request|
project_blob_path(merge_request.project, merge_request.head_pipeline_sha)
end
expose :base_path, if: -> (mr, _) { mr.base_pipeline_sha } do |merge_request|
project_blob_path(merge_request.project, merge_request.base_pipeline_sha)
end
end
expose :codeclimate, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:codequality) } do
expose :head_path do |merge_request|
head_pipeline_downloadable_path_for_report_type(:codequality)
end
expose :base_path do |merge_request|
base_pipeline_downloadable_path_for_report_type(:codequality)
end
end
expose :browser_performance, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:browser_performance) } do expose :browser_performance, if: -> (mr, _) { head_pipeline_downloadable_path_for_report_type(:browser_performance) } do
expose :degradation_threshold do |merge_request| expose :degradation_threshold do |merge_request|
merge_request.head_pipeline&.present(current_user: current_user) merge_request.head_pipeline&.present(current_user: current_user)
...@@ -145,15 +125,5 @@ module EE ...@@ -145,15 +125,5 @@ module EE
::BlockingMergeRequestEntity.represent(blocking_mr, blocking_mr_options) ::BlockingMergeRequestEntity.represent(blocking_mr, blocking_mr_options)
end end
def head_pipeline_downloadable_path_for_report_type(file_type)
object.head_pipeline&.present(current_user: current_user)
&.downloadable_path_for_report_type(file_type)
end
def base_pipeline_downloadable_path_for_report_type(file_type)
object.base_pipeline&.present(current_user: current_user)
&.downloadable_path_for_report_type(file_type)
end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment