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
self.update_column(:file_store, file.object_store)
end
def self.associated_file_types_for(file_type)
return unless file_types.include?(file_type)
[file_type]
end
def self.total_size
self.sum(:size)
end
......
......@@ -299,6 +299,12 @@ module Ci
project: [merge_request.source_project, merge_request.target_project])
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
# limited to a number of references.
#
......@@ -624,6 +630,45 @@ module Ci
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?
project.deployment_platform&.active?
end
......
......@@ -110,6 +110,17 @@ module Ci
merge_request_presenter&.target_branch_link
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
def plain_ref_name
......
......@@ -85,6 +85,26 @@ class MergeRequestWidgetEntity < Grape::Entity
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
delegate :current_user, to: :request
......@@ -103,6 +123,16 @@ class MergeRequestWidgetEntity < Grape::Entity
can?(current_user, :read_build, merge_request.source_project) &&
can?(current_user, :create_pipeline, merge_request.source_project)
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
MergeRequestWidgetEntity.prepend_if_ee('EE::MergeRequestWidgetEntity')
......@@ -91,16 +91,6 @@ module EE
!merge_train_pipeline? && super
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?
batch_lookup_report_artifact_for_file_type(:license_scanning).present?
end
......@@ -173,35 +163,6 @@ module EE
def merge_train_ref?
::MergeRequest.merge_train_ref?(ref)
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
......@@ -28,17 +28,6 @@ module EE
batch_lookup_report_artifact_for_file_type(:container_scanning)
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)
if (job_artifact = batch_lookup_report_artifact_for_file_type(file_type)) &&
can?(current_user, :read_build, job_artifact.job)
......
......@@ -6,26 +6,6 @@ module EE
extend ActiveSupport::Concern
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 :degradation_threshold do |merge_request|
merge_request.head_pipeline&.present(current_user: current_user)
......@@ -145,15 +125,5 @@ module EE
::BlockingMergeRequestEntity.represent(blocking_mr, blocking_mr_options)
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
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