Manage Lifecycles and Statuses
Lifecycles and statuses can be managed by maintainers of a root namespace (top-level group). It's a settings page (like custom fields) under `Issues` (soon to be renamed to `Work items`). In the first iteration it's probably the same page as custom fields and we'll use sections for status and custom fields. ### From the parent epic acceptance criteria - [ ] Navigate to the root group's Settings > Work Items > Status. - [ ] View the default statuses grouped by their category - [ ] Update status names - [ ] Create a new status - [ ] Reorder statuses within a category - [ ] Set a default status in each state (open/closed) - [ ] Delete a status - [ ] ~Map soon-to-be deleted status to other existing status.~ [Not part of Iteration 2](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items_custom_status/#:~:text=As%20part%20of,but%20not%20deleted.) - [ ] ~Update all corresponding work items with the deleted status to the new status~ [Not part of Iteration 2](https://handbook.gitlab.com/handbook/engineering/architecture/design-documents/work_items_custom_status/#:~:text=As%20part%20of,but%20not%20deleted.) - [ ] Status configuration is only available for ~"GitLab Premium" and above. - [ ] Cascade and apply configuration to all child namespaces (subgroups and projects) - [ ] Instrument event tracking for create, update, delete status (record status and category name as custom attributes) [MAU/WAU + Total Counts) - [ ] Status configuration and interactions are available in our GraphQL API (public). What we need to perform these actions in the frontend fulfills this requirement. ### Non-goals (because part of iteration 3) 1. Manage multiple lifecycles 1. Create new lifecycles 2. Attach work item types to lifecycles For now we can assume two different "screens": 1. **Lifecycle overview**: Lists lifecycles and the work item types that are attached to it and its statuses. It only displays data, no actions just buttons. You can then edit the lifecycle. ![image](/uploads/132b6901e5f5bf167ba257e5c82a9f42/image.png) 3. **Edit lifecycle** shows a list of status categories and their statuses under those categories. Statuses can be reordered within their category. Status categories are hard coded. The category of a status cannot be changed for now. Actions: 1. Attach status to lifecycle 2. Change a status (maybe through modal because status is a namespace global entity and changes apply to all usages of it in different lifecycles) 3. Reorder statuses within their category 4. Set open/closed/duplicated defaults 5. remove status (from the lifecycle). ![image](/uploads/9037d02eac19f1409e92dd5eeefc401b/image.png) And additional modals for: 1. **Remove status (from lifecycle)**: The user needs to provide a new status for the items that currently have this status. If it's a default status (open/closed/duplicate), the user needs to set a new default. ## Lifecycle overview We could form a single GraphQL API call to a new endpoint `lifecycles` and request everything that we're interested in. For example: ``` query getLifecycles { namespace(fullPath: "flightjs") { id lifecycles { id name default_open_status default_closed_status default_duplicated_status # Maybe no connection type needed? workItemTypes { id name iconName } # Using only statuses would allow us to reuse existing approaches of # just having a list of statuses. statuses { id name iconName color # We need this to distribute statuses to its categories category # Maybe we don't need to expose it because it's already sorted correctly position } } } } ``` ## Edit lifecycle For status updates, only provide list of arrays. Backend will calculate new position and assignment based on that list. ## Add status Add status separately: ``` mutation createStatus($input: CreateStatusInput!) { StatusesCreate(input: $input) { status { id name iconName color category } errors } } ``` 1. Fetch all lifecycles and assigned statuses in a lifecycle query for the namespace 1. Use lifecycle mutation to update the full lifecycle including assigned statuses. 1. Then pass everything to a `Lifecycle::UpdateService`. When it's first run for a namespace (when it doesn't have any custom lifecycles), we initiate the migration from system-defined to custom statuses/lifecycles. 2. We'll add all records for custom status and add an entry for each status migration (for example system defined status 3 to custom status 5) and enqueue a job to update work items. 3. The job performs updates in batches (without callbacks) using the namespace_id and the status id on `current_statuses`. 4. Once done it removes all migration records. 5. The lifecycle may have a field called `active_migration` which is a boolean to indicate whether a migration is ongoing (for the frontend). 6. TBD whether we'll do some kind of union query for that time for lists so filters continue to work as expected and/or modify select queries to map to new statuses... probably not part of initial iteration 3. Fetch all statuses using a status query for the namespace 4. Use a status mutation to add a new status and rename statuses 1. Question: What happens when a status is not used in any lifecycle? Do we "soft" delete it then? Or keep it forever? Frontend handles displaying of the lifecycle and statuses. When adding a status to a lifecycle, the user can choose from existing statuses (if at least one status which is not part of the lifecycle is available). There'll also be the option to create a new status.
epic