Adjust lifecycleUpdate mutation to prevent removal of mapped statuses

What does this MR do and why?

Currently, the lifecycleUpdate mutation (introduced in !192988 (merged)) prevents deletion of a custom status in the following cases:

  1. The status is in use (i.e., explicitly assigned to a work item).
  2. The status is set as one of the lifecycle's default statuses (e.g., open, closed, duplicated).

This MR adds support for a third condition:

  1. A status cannot be deleted if it was created during the transition from system-defined to custom statuses and has a recorded mapping. These mapped statuses (e.g., To do, Done, Duplicate) can still be updated but not removed.

Additionally, this MR fixes a minor bug where the status mapping (converted_from_system_defined_status_identifier) was not being recorded when creating a custom status during the switch from system-defined to statuses.

References

Screenshots or screen recordings

When trying to delete a status when transitioning from a system-defined to a custom lifecycle

Screenshot_2025-06-18_at_10.04.52_am

When trying to delete a status in use - that either is explicitly assigned to a work item or has an existing mapping

Screenshot_2025-06-16_at_12.58.52_pm

When trying to delete a status that is set as one of the lifecycle's default statuses

Screenshot_2025-06-16_at_1.14.49_pm

When a status is deleted successfully

Screenshot_2025-06-16_at_1.16.39_pm

Screenshot_2025-06-16_at_1.17.17_pm

How to set up and validate locally

  1. Enable the work_item_status_feature_flag feature flag.
  2. Run a mutation to create a custom lifecycle and statuses from the system-defined ones.
View mutation
mutation UpdateLifecycle($input: LifecycleUpdateInput!) {
  lifecycleUpdate(input: $input) {
    lifecycle {
      id
      name
      statuses {
        id
        name
      }
      defaultOpenStatus {
        id
        name
      }
      defaultClosedStatus {
        id
        name
      }
      defaultDuplicateStatus {
        id
        name
      }
    }
    errors
  }
}
{
  "input": {
    "namespacePath": "gitlab-org",
    "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Lifecycle/1",
    "statuses": [
      {
        "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/1",
        "name": "To do",
        "color": "#737278",
        "description": null,
        "category": "TO_DO"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/2",
        "name": "In progress",
        "color": "#1f75cb",
        "description": null,
        "category": "IN_PROGRESS"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/3",
        "name": "Done",
        "color": "#108548",
        "description": null,
        "category": "DONE"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/4",
        "name": "Won't do",
        "color": "#DD2B0E",
        "description": null,
        "category": "CANCELLED"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/5",
        "name": "Duplicate",
        "color": "#DD2B0E",
        "description": null,
        "category": "CANCELLED"
      }
    ],
      "defaultOpenStatusIndex": 0,
      "defaultClosedStatusIndex": 2,
      "defaultDuplicateStatusIndex": 4
  }
}
  1. Run a mutation to attempt to delete a status in use - that either is explicitly assigned to a work item or has an existing mapping.
View mutation
mutation UpdateLifecycle($input: LifecycleUpdateInput!) {
  lifecycleUpdate(input: $input) {
    lifecycle {
      id
      name
      statuses {
        id
        name
      }
      defaultOpenStatus {
        id
        name
      }
      defaultClosedStatus {
        id
        name
      }
      defaultDuplicateStatus {
        id
        name
      }
    }
    errors
  }
}
{
  "input": {
    "namespacePath": "gitlab-org",
    "id": "gid://gitlab/WorkItems::Statuses::Custom::Lifecycle/35",
    "statuses": [
      {
        "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/157",
        "name": "To do",
        "color": "#737278",
        "description": null,
        "category": "TO_DO"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/158",
        "name": "In progress",
        "color": "#1f75cb",
        "description": null,
        "category": "IN_PROGRESS"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/159",
        "name": "Done",
        "color": "#108548",
        "description": null,
        "category": "DONE"
      },
      {
        "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/161",
        "name": "Duplicate",
        "color": "#DD2B0E",
        "description": null,
        "category": "CANCELLED"
      }
    ],
      "defaultOpenStatusIndex": 0,
      "defaultClosedStatusIndex": 2,
      "defaultDuplicateStatusIndex": 3
  }
}

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.

Edited by Agnes Slota

Merge request reports

Loading