Add CountFrameworksWithRequirementsMetric
What does this MR do and why?
Add CountFrameworksWithRequirementsMetric
Changelog: other EE: true
References
Please include cross links to any resources that are relevant to this MR. This will give reviewers and future readers helpful context to give an efficient review of the changes introduced.
MR acceptance checklist
Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.
Screenshots or screen recordings
Screenshots are required for UI changes, and strongly recommended for all other merge requests.
| Before | After |
|---|---|
Query plan:
SELECT COUNT(DISTINCT "compliance_management_frameworks"."id")
FROM "compliance_management_frameworks"
INNER JOIN "compliance_requirements"
ON "compliance_requirements"."framework_id" = "compliance_management_frameworks"."id"
How to set up and validate locally
Setup local compliance frameworks with/without requirements and run this:
metric_frameworks = Gitlab::Usage::Metrics::Instrumentations::CountFrameworksWithRequirementsMetric.new({
time_frame: 'all'
})
puts "SQL query for frameworks with requirements: #{metric_frameworks.to_sql}"
puts "Value: #{metric_frameworks.value}"
Where time_frame can be 'all' '7d' '28d'
or use this script:
code
test_id = "test_#{Time.now.to_i}"
created_records = {
requirements: [],
frameworks: [],
namespace: nil
}
begin
puts "=== Testing CountFrameworksWithRequirementsMetric ==="
puts "Using test identifier: #{test_id}"
organization = Organizations::Organization.first
namespace = Group.create!(
name: "Test Group #{test_id}",
path: "test-group-#{test_id}",
organization: organization
)
created_records[:namespace] = namespace
frameworks = []
framework1 = ComplianceManagement::Framework.create!(
name: "Framework With Req #{test_id}",
description: "Framework with requirements",
color: "#FF0000",
namespace: namespace
)
frameworks << framework1
framework2 = ComplianceManagement::Framework.create!(
name: "Framework With Req Old #{test_id}",
description: "Framework with requirements (older)",
color: "#00FF00",
namespace: namespace,
created_at: 14.days.ago,
updated_at: 14.days.ago
)
frameworks << framework2
framework3 = ComplianceManagement::Framework.create!(
name: "Framework With Req Oldest #{test_id}",
description: "Framework with requirements (oldest)",
color: "#0000FF",
namespace: namespace,
created_at: 30.days.ago,
updated_at: 30.days.ago
)
frameworks << framework3
framework4 = ComplianceManagement::Framework.create!(
name: "Framework No Req #{test_id}",
description: "Framework without requirements",
color: "#FFFF00",
namespace: namespace
)
frameworks << framework4
created_records[:frameworks] = frameworks
frameworks.each { |f| puts "- #{f.name} (ID: #{f.id})" }
requirements = []
req1 = ComplianceManagement::ComplianceFramework::ComplianceRequirement.create!(
framework: framework1,
namespace: namespace,
name: "Requirement for F1 #{test_id}",
description: "Current requirement"
)
requirements << req1
req2 = ComplianceManagement::ComplianceFramework::ComplianceRequirement.create!(
framework: framework2,
namespace: namespace,
name: "Requirement for F2 #{test_id}",
description: "14-day old requirement",
created_at: 14.days.ago,
updated_at: 14.days.ago
)
requirements << req2
req3 = ComplianceManagement::ComplianceFramework::ComplianceRequirement.create!(
framework: framework3,
namespace: namespace,
name: "Requirement for F3 #{test_id}",
description: "30-day old requirement",
created_at: 30.days.ago,
updated_at: 30.days.ago
)
requirements << req3
created_records[:requirements] = requirements
class TestCountFrameworksWithRequirementsMetric
def initialize(framework_ids, time_frame)
@framework_ids = framework_ids
@time_frame = time_frame
end
def value
case @time_frame
when 'all'
ComplianceManagement::Framework
.where(id: @framework_ids)
.joins(:compliance_requirements)
.distinct
.count
when '28d'
ComplianceManagement::Framework
.where(id: @framework_ids)
.where(created_at: 28.days.ago..Time.current)
.joins(:compliance_requirements)
.distinct
.count
when '7d'
ComplianceManagement::Framework
.where(id: @framework_ids)
.where(created_at: 7.days.ago..Time.current)
.joins(:compliance_requirements)
.distinct
.count
end
end
end
puts "\n=== Testing Metrics ==="
time_frames = %w[all 28d 7d]
time_frames.each do |time_frame|
expected = case time_frame
when 'all'
3 # All 3 frameworks with requirements
when '28d'
2 # Framework 1 and 2 (excluding the 30-day old one)
when '7d'
1 # Only framework 1 (current)
end
test_metric = TestCountFrameworksWithRequirementsMetric.new(
frameworks.map(&:id),
time_frame
)
value = test_metric.value
puts "\n---------------------------"
puts "Time frame: #{time_frame}"
puts "Value: #{value} (Expected: #{expected})"
puts "Status: #{value == expected ? 'PASS ✓' : 'FAIL ✗'}"
real_metric = Gitlab::Usage::Metrics::Instrumentations::CountFrameworksWithRequirementsMetric.new({
time_frame: time_frame
})
puts "\nFor comparison, actual metric implementation returns:"
puts "SQL: #{real_metric.to_sql}"
puts "Value includes your test data and any other data in the database"
end
puts "\n=== Test Complete ==="
rescue => e
puts "Error: #{e.message}"
puts e.backtrace.join("\n")
ensure
puts "\n=== Cleaning up test data ==="
created_records[:requirements].each(&:destroy!) if created_records[:requirements]
created_records[:frameworks].each(&:destroy!) if created_records[:frameworks]
created_records[:namespace]&.destroy!
puts "All test data cleaned up"
end
puts "Script completed"
Edited by Andrew Jung