3.4 KiB
+++
title = "Remove id field from ticket JSON body"
priority = 9
status = "done"
ticket_type = "task"
dependencies = []
+++
The ticket ID is already encoded in the filename (a3f9c2.json). Storing it redundantly inside the JSON body creates a potential consistency hazard (the two could disagree) and wastes space. The filename should be the sole source of truth for the ID.
Motivation
- Eliminates a redundancy: filename stem already IS the id.
- Removes the risk of id mismatch (e.g. if a file is renamed manually).
- Simplifies the JSON schema — consumers only need the body fields, not a duplicated key.
Serde approach
In ticket.rs, annotate the id field with #[serde(skip)]:
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Ticket {
#[serde(skip)]
pub id: String,
// ...
}
#[serde(skip)] means:
- Serialise: the
idfield is omitted from JSON output entirely. - Deserialise: the field is not read from JSON; it is initialised with
String::default()(empty string) and must be set manually after deserialization.
Because serde ignores unknown fields by default, existing files with "id": "..." in the JSON body continue to deserialise without error — the field is simply discarded. This means the change is backwards-compatible for reads; existing stores work immediately. A separate nbd migrate command (see companion ticket) cleans up the stale id field from disk.
store.rs changes
read_ticket(root, id) — inject id from the id parameter after deserialising:
let mut ticket: Ticket = serde_json::from_slice(&bytes)?;
ticket.id = id.to_string(); // authoritative source: the filename
Ok(ticket)
list_tickets(root) — inject id from each file's stem:
let stem = path.file_stem().and_then(|s| s.to_str()).ok_or("invalid filename")?;
let mut ticket: Ticket = serde_json::from_slice(&bytes)?;
ticket.id = stem.to_string();
tickets.push(ticket);
write_ticket — no change needed; #[serde(skip)] already prevents id from being written.
ticket_path — no change; it already takes id: &str as a separate parameter.
Impact on other tickets
- The
nbd migratecompanion ticket (see deps) provides the command to scrub the oldidfield from existing files. - Partial ID matching (
resolve_id) is unaffected — it works on filenames, not JSON content. - All display, list, and read commands continue to work;
ticket.idis populated from the filename in every read path.
Tests
Unit tests (src/tests.rs):
write_ticketoutput does NOT contain the"id"key.read_ticketwith a file that has NOidfield correctly setsticket.idfrom the parameter.read_ticketwith an old-format file (has"id"in JSON) still setsticket.idfrom the parameter (ignores JSON value).list_ticketsinjects correct ids from filenames for all tickets.- Serialisation roundtrip: write then read, id is preserved via filename not JSON.
Integration tests (tests/integration.rs):
nbd createoutput (tabular and--json) contains the correct ID.- The created
.jsonfile on disk does NOT contain the"id"key. nbd read <id>displays the correct ID.
Files touched
src/ticket.rs— add#[serde(skip)]toidsrc/store.rs—read_ticketandlist_ticketsinject id from filenamesrc/tests.rs— update and add unit teststests/integration.rs— add assertion that written files lack"id"key