[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
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
.
- 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)
- 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
- Connect custom status to all abstraction methods
- BE: Create tables and models for custom lifecycle and status
- Work Items Custom Status Design Doc
- Configurable Work Item Statuses Epic
Screenshots or screen recordings
backend changes only.
How to set up and validate locally
Scenario 1 - test with system-defined statuses
Prerequisites
- Enable the
work_item_status_feature_flag
feature flag. - Create a new task under the
flightjs/Flight
project. - 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
- 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
}
- 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
- Enable the
work_item_status_feature_flag
feature flag. - Create a new task under the
flightjs/Flight
project. - 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
- Create a custom lifecycle and statuses via Rails console.
Click to expand
- Find a namespace
project = Project.find(7) # flightjs/Flight
namespace = project.root_ancestor
- 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
)
- 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
)
- 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
)
- 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)
- 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
}
- 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"
}
-
⚠️ 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.