Skip to content

[Part 2] Connect custom status to all abstraction methods

What does this MR do and why?

As part of the configurable work item statuses initiative, this MR adds support to the Resolvers::WorkItems::Statuses::AllowedStatusesResolver to handle both system-defined and custom work item statuses. Before that, the resolver only handled the system-defined statuses.

This MR builds on top of [Part 1] Connect custom status to all abstraction methods, where the CurrentStatus model and BulkStatusResolver were updated to handle both system-defined and custom work item statuses.

References

Screenshots or screen recordings

backend changes only.

How to set up and validate locally

Prerequisites

  1. Enable the work_item_status_feature_flag feature flag.

Scenario 1 - test with system-defined statuses

  1. Run the test query.
View query
query namespaceWorkItemTypes($fullPath: ID!, $name: IssueType) {
  workspace: namespace(fullPath: $fullPath) {
    id
    workItemTypes(name: $name) {
      nodes {
        ...WorkItemTypeFragment
      }
    }
  }
}

fragment WorkItemTypeFragment on WorkItemType {
  id
  name
  iconName
  widgetDefinitions {
    type
    ... on WorkItemWidgetDefinitionStatus {
      allowedStatuses {
        id
        name
        iconName
        color
        position
      }
    }
  }
}
{
  "fullPath": "flightjs/Flight",
  "name": "TASK"
}
  1. Verify the response. Expect the allowed statuses to be system-defined.
View response
{
  "data": {
    "workspace": {
      "id": "gid://gitlab/Namespaces::ProjectNamespace/34",
      "workItemTypes": {
        "nodes": [
          {
            "id": "gid://gitlab/WorkItems::Type/5",
            "name": "Task",
            "iconName": "issue-type-task",
            "widgetDefinitions": [
              {
                "type": "ASSIGNEES"
              },
              {
                "type": "AWARD_EMOJI"
              },
              {
                "type": "CRM_CONTACTS"
              },
              {
                "type": "CURRENT_USER_TODOS"
              },
              {
                "type": "DESCRIPTION"
              },
              {
                "type": "DEVELOPMENT"
              },
              {
                "type": "HIERARCHY"
              },
              {
                "type": "ITERATION"
              },
              {
                "type": "LABELS"
              },
              {
                "type": "LINKED_ITEMS"
              },
              {
                "type": "MILESTONE"
              },
              {
                "type": "NOTES"
              },
              {
                "type": "NOTIFICATIONS"
              },
              {
                "type": "PARTICIPANTS"
              },
              {
                "type": "START_AND_DUE_DATE"
              },
              {
                "type": "TIME_TRACKING"
              },
              {
                "type": "WEIGHT"
              },
              {
                "type": "STATUS",
                "allowedStatuses": [
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/1",
                    "name": "To do",
                    "iconName": "status-waiting",
                    "color": "#737278",
                    "position": 0
                  },
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/2",
                    "name": "In progress",
                    "iconName": "status-running",
                    "color": "#1f75cb",
                    "position": 0
                  },
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/3",
                    "name": "Done",
                    "iconName": "status-success",
                    "color": "#108548",
                    "position": 0
                  },
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/4",
                    "name": "Won't do",
                    "iconName": "status-cancelled",
                    "color": "#DD2B0E",
                    "position": 0
                  },
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/5",
                    "name": "Duplicate",
                    "iconName": "status-cancelled",
                    "color": "#DD2B0E",
                    "position": 10
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  },
  "correlationId": "01JRSFJM0RZ52V9RX5WHTFCER3"
}

Scenario 2 - test with custom statuses

  1. Create a custom lifecycle and statuses via Rails console.
Click to expand
  1. Find a namespace
project = Project.find(7) # flightjs/Flight
namespace = project.root_ancestor
  1. Create custom statuses for a given namespace
open_status = WorkItems::Statuses::Custom::Status.create!(
  namespace: namespace,
  name: "To do",
  color: "#737278",
  category: :to_do
)

closed_status = WorkItems::Statuses::Custom::Status.create!(
  namespace: namespace,
  name: "Done",
  color: "#108548",
  category: :done
)

duplicate_status = WorkItems::Statuses::Custom::Status.create!(
  namespace: namespace,
  name: "Duplicate",
  color: "#DD2B0E",
  category: :cancelled
)
  1. Create a custom lifecycle with default statuses
lifecycle = WorkItems::Statuses::Custom::Lifecycle.create!(
  namespace: namespace,
  name: "Engineering",
  default_open_status: open_status,
  default_closed_status: closed_status,
  default_duplicate_status: duplicate_status
)
  1. Associate work item types with the lifecycle (namespace is automatically populated)
task_type = WorkItems::Type.find_by(base_type: :task)

type_custom_lifecycle = WorkItems::TypeCustomLifecycle.create!(
  work_item_type: task_type,
  lifecycle: lifecycle
)
  1. Run the test query.
View query
query namespaceWorkItemTypes($fullPath: ID!, $name: IssueType) {
  workspace: namespace(fullPath: $fullPath) {
    id
    workItemTypes(name: $name) {
      nodes {
        ...WorkItemTypeFragment
      }
    }
  }
}

fragment WorkItemTypeFragment on WorkItemType {
  id
  name
  iconName
  widgetDefinitions {
    type
    ... on WorkItemWidgetDefinitionStatus {
      allowedStatuses {
        id
        name
        iconName
        color
        position
      }
    }
  }
}
{
  "fullPath": "flightjs/Flight",
  "name": "TASK"
}
  1. Verify the response. Expect the allowed statuses to be custom.
View response
{
  "data": {
    "workspace": {
      "id": "gid://gitlab/Namespaces::ProjectNamespace/34",
      "workItemTypes": {
        "nodes": [
          {
            "id": "gid://gitlab/WorkItems::Type/5",
            "name": "Task",
            "iconName": "issue-type-task",
            "widgetDefinitions": [
              {
                "type": "ASSIGNEES"
              },
              {
                "type": "AWARD_EMOJI"
              },
              {
                "type": "CRM_CONTACTS"
              },
              {
                "type": "CURRENT_USER_TODOS"
              },
              {
                "type": "DESCRIPTION"
              },
              {
                "type": "DEVELOPMENT"
              },
              {
                "type": "HIERARCHY"
              },
              {
                "type": "ITERATION"
              },
              {
                "type": "LABELS"
              },
              {
                "type": "LINKED_ITEMS"
              },
              {
                "type": "MILESTONE"
              },
              {
                "type": "NOTES"
              },
              {
                "type": "NOTIFICATIONS"
              },
              {
                "type": "PARTICIPANTS"
              },
              {
                "type": "START_AND_DUE_DATE"
              },
              {
                "type": "TIME_TRACKING"
              },
              {
                "type": "WEIGHT"
              },
              {
                "type": "STATUS",
                "allowedStatuses": [
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/11",
                    "name": "To do",
                    "iconName": "status-waiting",
                    "color": "#737278",
                    "position": 0
                  },
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/12",
                    "name": "Done",
                    "iconName": "status-success",
                    "color": "#108548",
                    "position": 0
                  },
                  {
                    "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/13",
                    "name": "Duplicate",
                    "iconName": "status-cancelled",
                    "color": "#DD2B0E",
                    "position": 0
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  },
  "correlationId": "01JRSF642EGNF30X5V88GCB412"
}
  1. ⚠️ Since there are still a few tasks remaining to fully support custom statuses, please remember to clean up any custom status-related records after testing. You can use the command below to remove custom statuses and all associated records:
WorkItems::Statuses::Custom::Status.destroy_all

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