Helpdesk Ticket States: Renaming a State Under Open Tickets Without Moving One
Workflow states are reference data with consequences. A helpdesk ships states like new, open, resolved and closed. Every ticket points at one, and each state names the one that usually comes next, so the application can offer a “move on” button. Renaming a state, or adding one in the middle, is a release-day job that usually ends in a hand-written migration.
The workflow example does it with seed files. The states table refers to itself: next_id holds the id of the next state, and the seed writes it by code.
[refs]
next_id = "states.code"
[[row]]
code = "done"
_renamed_from = "resolved"
label = "Done"
next_id = "closed"
The plan for release 2, from bst plan.
The Rename
Release 1 ships the four states. Two tickets come in, one sitting in resolved, and the admin relabels Open as “In progress”. Release 2 renames resolved to done and adds a waiting state, for tickets waiting on the customer, between open and done.
Without _renamed_from, a new code looks like one row dropped and another added. The dropped row would be kept, retired or deleted, and the ticket in it would be stranded or broken. With it, Bootstrapping updates the existing row’s code in place. Same row, same id, so the ticket that was resolved is now done without a single write to the tickets table. The example checks every ticket’s state id before and after, and they match.
The Chain
The new state needs the renamed one: waiting’s next state is done, a code that exists only once the rename has run. Bootstrapping writes a table’s references to itself after its inserts and renames, so the order inside the seed file doesn’t matter. Open moves on to waiting, waiting to done, and the chain walks new, open, waiting, done, closed, which the example follows with a recursive query.
The admin’s “In progress” label stays, since only the admin changed it. The renamed state takes the seed’s new label, Done, since nobody had touched Resolved. And closed is owned by the seed, because the application decides whether a ticket is finished by that flag and an admin shouldn’t be able to flip it by accident. Writing this example turned up one gap, fixed in 0.2.1: a row that leaves out a NOT NULL column like closed used to pass the plan and fail at the insert. The plan now names the row.
Run it with go run ./examples/workflow from a clone of the repository, or read the other case studies.