Skip to content

feat!: sample-import record ids are strings - #22

Merged
mhusbynflow merged 4 commits into
masterfrom
mhusbyn/flow-725-string-ids
Aug 18, 2026
Merged

feat!: sample-import record ids are strings#22
mhusbynflow merged 4 commits into
masterfrom
mhusbyn/flow-725-string-ids

Conversation

@mhusbynflow

Copy link
Copy Markdown
Collaborator

Companion to goodwright/flow-api#267 (FLOW-725). Merge this first — that PR's acceptance fixtures expect these types.

Why

The Flow API now serialises v2 record ids as JSON strings. Ids are 18-digit BigInteger primary keys, far past what JavaScript represents exactly, so an integer on the wire is silently corrupted in any browser client.

Python has no such limit, so pydantic's lax mode was quietly coercing the new strings back to int and nothing broke. But the declared types then lied about the wire, and the tests asserted a shape the server no longer sends.

What changed

  • SampleImportJob.id, .sample_ids and .execution_id are now str. There was already precedent in this file: PubMedId is a str documented as "a bare integer written as a string".
  • Fixtures now send strings, so the round trip is proven rather than assumed.
  • --job-id still rejects non-numeric input, and now also rejects non-ASCII digits. '٤٢'.isdigit() is True while int('٤٢') is 42, so a naive digit check would have forwarded the raw Arabic-Indic string as the job id — a silently wrong value. Regression test included.
  • coerce_numbers_to_str=True means an older server that still sends integer ids continues to parse. A client is upgraded independently of the deployment it talks to, and without this a released 0.12.0 pointed at a not-yet-upgraded install would fail every samples import / import-status call with an uncaught ValidationError. An 18-digit id round-trips exactly.

BREAKING CHANGE

SampleImportJob.id, .sample_ids and .execution_id are now str, and samples import --json emits them quoted. Version 0.11.10.12.0.

Consumers reading those fields out of --json with jq will see "42" where they saw 42.

Verified

pytest tests/unit/ → 395 passed.

The API serialises record ids as strings: they are 18-digit values, far past
what JavaScript represents exactly, so an integer on the wire is corrupted in
any browser client. Python has no such limit, but declaring these int while the
wire says string left pydantic silently coercing and the tests asserting a shape
the server no longer sends.

Fixtures now send strings, so the round trip is proven rather than assumed.
--job-id still rejects non-numeric input: ids are always digits, and a
non-numeric one would fail deep inside the server's query building.

BREAKING CHANGE: SampleImportJob.id, .sample_ids and .execution_id are now str,
and 'samples import --json' emits them quoted.
str.isdigit() is true for digits from any script, so the guard admitted
values it was written to stop. '²³' passes isdigit() but is not parseable
as a number at all, reaching the server exactly as the guard exists to
prevent. '٤٢' is worse: it passes, and now that ids travel as strings it
is forwarded verbatim rather than normalised, silently addressing a
different job than the one asked for.

Requiring ASCII alongside isdigit() also rejects '-5' and ' 42', which the
previous int() parse accepted. Job ids are never negative or space-padded,
so that tightening is intended.
…ids yet

Pydantic does not coerce int -> str in lax mode, so SampleImportJob raised
ValidationError against any deployment still on the integer-id API — the
normal state of an on-prem install caught between client and server
upgrades. Sets coerce_numbers_to_str on the model; Python's unbounded ints
mean no precision is lost converting an 18-digit id. frozen moves into
model_config alongside it.
job.sample_ids is list[str] now that the API serialises record ids as
strings; str() on each element was a no-op.
@mhusbynflow
mhusbynflow merged commit 2a616b7 into master Aug 18, 2026
3 checks passed
@mhusbynflow
mhusbynflow deleted the mhusbyn/flow-725-string-ids branch August 18, 2026 16:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant