Skip to content

[Part 1] 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 WorkItems::Statuses::CurrentStatus model and Resolvers::WorkItems::Statuses::BulkStatusResolver to handle both system-defined and custom work item statuses. Before the changes, the model and resolver only handled the system-defined statuses.

The work will continue via [Part 2] Connect custom status to all abstraction methods, where support for both system-defined and custom statuses will be added to the Resolvers::WorkItems::Statuses::AllowedStatusesResolver.

SQL queries

⚠️ We haven't backfilled any work item status data yet, so there isn't enough production data available to run meaningful tests and provide query plans.

As shown here, the recently introduced work_item_custom_statuses table via this MR doesn't have any production data yet.

However, as outlined in this this issue, we plan to run query plans in production to verify that Resolvers::WorkItems::Statuses::BulkStatusResolver efficiently batch-loads work item statuses once the batched background migration is complete.

Local testing

Below are examples of the SQL queries produced by the Resolvers::WorkItems::Statuses::BulkStatusResolver.

  1. With work items that only have system-defined statuses assigned
WorkItems::Statuses::CurrentStatus Load (0.4ms)
SELECT "work_item_current_statuses".* 
FROM "work_item_current_statuses" 
WHERE "work_item_current_statuses"."work_item_id" IN (798, 797, 796, 795, 794)
  1. With work items that have a mix of system-defined and custom statuses assigned
WorkItems::Statuses::CurrentStatus Load (0.1ms)
SELECT "work_item_current_statuses".* 
FROM "work_item_current_statuses" 
WHERE "work_item_current_statuses"."work_item_id" IN (798, 797, 796, 795, 794)

WorkItems::Statuses::Custom::Status Load (0.4ms)
SELECT "work_item_custom_statuses".* 
FROM "work_item_custom_statuses" 
WHERE "work_item_custom_statuses"."id" IN (32, 34)

Production query plans

The only production query plan that we can provide at the moment only covers the first scenario where work items have system-defined statuses assigned.

References

Screenshots or screen recordings

backend changes only.

How to set up and validate locally

Scenario 1 - test with system-defined statuses

Prerequisites

  1. Enable the work_item_status_feature_flag feature flag.
  2. Create a new task under the flightjs/Flight project.
  3. Use the /status quick action or status widget to assign a system-defined status to the task. This is required to create a current status record.

Steps

  1. Run the test query.
View query
query workItemStatus($fullPath: ID!, $iid: String!) {
  workspace: namespace(fullPath: $fullPath) {
    id
    workItem(iid: $iid) {
      id
      widgets {
        type
        ... on WorkItemWidgetStatus {
          status {
            ...WorkItemStatusFragment
          }
        }
      }
    }
  }
}

fragment WorkItemStatusFragment on WorkItemStatus {
  id
  name
  iconName
  color
  position
}
{
  "fullPath": "flightjs/Flight",
  "iid": "41" # replace with the IID of your task
}
  1. Verify the response. Expect the work item status to be system-defined.
View response
{
  "data": {
    "workspace": {
      "id": "gid://gitlab/Namespaces::ProjectNamespace/34",
      "workItem": {
        "id": "gid://gitlab/WorkItem/757",
        "widgets": [
          {
            "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",
            "status": {
              "id": "gid://gitlab/WorkItems::Statuses::SystemDefined::Status/1",
              "name": "To do",
              "iconName": "status-waiting",
              "color": "#737278",
              "position": 0
            }
          }
        ]
      }
    }
  },
  "correlationId": "01JRRXQEW8S35FHGDZ8HSWJRTJ"
}

Scenario 2 - test with custom statuses

Prerequisites

  1. Enable the work_item_status_feature_flag feature flag.
  2. Create a new task under the flightjs/Flight project.
  3. Use the /status quick action or status widget to assign a system-defined status to the task. This is required to create a current status record.

Steps

  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. Assign a custom status to the work item's current status.
work_item = WorkItem.last

current_status = work_item.current_status

custom_status = WorkItems::Statuses::Custom::Status.last

current_status.update!(custom_status: custom_status)
  1. Run the test query.
View query
query workItemStatus($fullPath: ID!, $iid: String!) {
  workspace: namespace(fullPath: $fullPath) {
    id
    workItem(iid: $iid) {
      id
      widgets {
        type
        ... on WorkItemWidgetStatus {
          status {
            ...WorkItemStatusFragment
          }
        }
      }
    }
  }
}

fragment WorkItemStatusFragment on WorkItemStatus {
  id
  name
  iconName
  color
  position
}
{
  "fullPath": "flightjs/Flight",
  "iid": "41" # replace with the IID of your task
}
  1. Verify the response. Expect the work item status to be custom.
View response
{
  "data": {
    "workspace": {
      "id": "gid://gitlab/Namespaces::ProjectNamespace/34",
      "workItem": {
        "id": "gid://gitlab/WorkItem/757",
        "widgets": [
          {
            "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",
            "status": {
              "id": "gid://gitlab/WorkItems::Statuses::Custom::Status/11",
              "name": "To do",
              "iconName": "status-waiting",
              "color": "#737278",
              "position": 0
            }
          }
        ]
      }
    }
  },
  "correlationId": "01JRRZQ116XFAMJFN542YQ0Z25"
}
  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