Refresh requirement statuses on control deletion

What does this MR do and why?

Whenever a compliance control is destroyed all the associated project requirement status entries will be refreshed or deleted.

References

How to set up and validate locally

  1. You need to have a group with Ultimate license.
  2. Under the group, create at least one project.
  3. Create a framework for the group by following steps here.
  4. Apply framework to the project by following steps.
  5. Create a compliance requirement with controls for the framework by running following graphql mutation:
mutation createComplianceRequirementWithControls {
  createComplianceRequirement(
    input: {
      complianceFrameworkId: "gid://gitlab/ComplianceManagement::Framework/<framework_id>",
      params: {
        name: "Requirement1",
        description: "some description"
      },
      controls: [
        {
          expression: "{\"operator\":\"=\",\"field\":\"project_visibility\",\"value\":\"private\"}",
          name: "project_visibility_not_internal"
        },
        {
          expression: "{\"operator\":\"=\",\"field\":\"minimum_approvals_required\",\"value\":2}",
          name: "minimum_approvals_required_2"
        }
      ]
    }) {
    errors
    requirement {
      id
      name
      description
      complianceRequirementsControls {
        nodes {
          id
          expression
          name
        }
      }
    }
  }
}
  1. You can get the list of created requirements and controls created by running following query, you can use the ids listed here for next steps.
query namespace {
  namespace(fullPath: "<group_full_path>") {
    id
    name
    complianceFrameworks {
      nodes {
        id
        name
        complianceRequirements {
          nodes {
            id
            name
            description
            complianceRequirementsControls {
              nodes {
                id
                name
                expression
              }
            }
          }
        }
      }
    }
  }
}
  1. Now open rails console and run following command to create entries for ProjectControlComplianceStatus model. We need to create control statuses for the project and all the controls belonging to the newly added requirements for the framework. I did something like following, you need to replace ids with your own.
## Check if requirement status exists for the requirement id, it should return empty array
::ComplianceManagement::ComplianceFramework::ProjectRequirementComplianceStatus.where(compliance_requirement_id: 21)

## Create control statuses, replace ids with your own values

::ComplianceManagement::ComplianceFramework::ProjectControlComplianceStatus.create!(project_id: 24, namespace_id: 33, compliance_requirement_id: 21, compliance_requirements_control_id: 24, status: 'pass')
::ComplianceManagement::ComplianceFramework::ProjectControlComplianceStatus.create!(project_id: 24, namespace_id: 33, compliance_requirement_id: 21, compliance_requirements_control_id: 23, status: 'pending')

## Search the requirement status again, it will return an entry
::ComplianceManagement::ComplianceFramework::ProjectRequirementComplianceStatus.where(compliance_requirement_id: 21)

=> [#<ComplianceManagement::ComplianceFramework::ProjectRequirementComplianceStatus:0x0000000306f11cc0
  id: 5,
  created_at: Thu, 03 Apr 2025 12:35:23.629835000 UTC +00:00,
  updated_at: Thu, 03 Apr 2025 12:35:23.629835000 UTC +00:00,
  project_id: 24,
  namespace_id: 33,
  compliance_requirement_id: 21,
  compliance_framework_id: 2,
  pass_count: 1,
  fail_count: 0,
  pending_count: 1>]

## Now get the control object and the user objects

control = ComplianceManagement::ComplianceFramework::ComplianceRequirementsControl.find(23) # This is one of the control for which control status was created above
user = User.find(1) # For local gdk, this is the id of the owner of the namespace, replace it with yours

## Now destroy the control with destroy service

ComplianceManagement::ComplianceFramework::ComplianceRequirementsControls::DestroyService.new(control: control, current_user: user).execute

## If you check the requirement status again, it would have changed, for me the pending_count decreased from 1 to 0.

::ComplianceManagement::ComplianceFramework::ProjectRequirementComplianceStatus.where(compliance_requirement_id: 21)

=> [#<ComplianceManagement::ComplianceFramework::ProjectRequirementComplianceStatus:0x0000000306ebb190
  id: 5,
  created_at: Thu, 03 Apr 2025 12:35:23.629835000 UTC +00:00,
  updated_at: Thu, 03 Apr 2025 12:43:06.768940000 UTC +00:00,
  project_id: 24,
  namespace_id: 33,
  compliance_requirement_id: 21,
  compliance_framework_id: 2,
  pass_count: 1,
  fail_count: 0,
  pending_count: 0>]

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

Related to #527087 (closed)

Edited by Hitesh Raghuvanshi

Merge request reports

Loading