Skip to content

Add A ResourceID Component For Record Inputs - #65

Merged
dinoconstantinou87 merged 3 commits into
mainfrom
feat/resource-id
Oct 3, 2026
Merged

dinoconstantinou87 merged 3 commits into
mainfrom
feat/resource-id

Conversation

@dinoconstantinou87

@dinoconstantinou87 dinoconstantinou87 commented Oct 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

The CLI sends bare IDs to nearly every input that names a record, and FreeAgent accepts them, but the spec said so in only three places. Every such input was a plain string with a full-URL example. This adds one ResourceID schema - a record's URL or its ID - and uses it for every input that names a record, after probing each one with a bare ID.

  • Query filters (20): contact, project, user, task, invoice and bank_account on every list that takes them, plus createTask's ?project.
  • Body fields (43): every payload reference - contact, project, category, user, task, bank_account, bank_transaction, paid_bill, paid_invoice, paid_user, capital_asset_type, and the item and entry urls - plus estimate and invoice on item create.
  • Path ids: the 38 identical inline id parameters become one shared ID parameter, as the self assessment user_id already was. Its schema is ResourceID, and its description says a path refuses the URL. That one is renamed UserID to match, and points at ResourceID too.
  • Responses are untouched. They always carry full URLs, so their fields keep URL examples.
  • The spec moves to OpenAPI 3.1.0, so field descriptions beside the new $refs still reach the generated doc comments. See below.
  • Three inputs are left as they were: credit note item id, which refuses a URL, and journal entry stock_item and property, which the sandbox can't test.

What The Sandbox Showed

Probed under the pinned X-Api-Version: 2026-09-01, no header and 2024-10-01; nothing differs between them. Every probe record was deleted, and the opening balances set's test entries were removed again.

  • Paths take only the ID. A URL in the path is a 400 URL-encoded and a 404 raw. That was checked on contacts under all three versions, and on all 15 resources with a show route under the pinned one. The self assessment user_id path is a 404 with an encoded URL.
  • Every query filter takes a bare ID, returning the same records as the URL, while an unknown ID matches nothing or is a 404. The five project filters with no data had a probe project made for them. invoice on estimates was tested with an estimate sent and converted.
  • Every body reference takes a bare ID and comes back as the right URL. That covers create and update for projects, timeslips, bills and expenses, create for credit notes, estimates and invoices, journal entry user, opening balance contact, and explanation paid_user, paid_bill and category on update. The rest were already proven when the CLI switched to ResourceID (Accept An ID Or URL Everywhere #49) and in later coverage PRs.
  • Credit note item id refuses a URL. A full item URL is a 404 for both an update and _destroy; the bare ID works. It stays ID-only, and its description now says so.
  • Journal entry stock_item and property are untested. The sandbox has no stock items or properties, so they stay URL-only.

Code

  • components/schemas/ResourceID.yaml: a string, "A record's URL or its ID", with example '1'.
  • components/parameters/ID.yaml: the shared path id, replacing an inline copy in 17 path files.
  • Descriptions: filters no longer say "URL". "URL or ID" went from createTask's project, BillItemPayload.url and JournalEntryPayload.url, since the schema says it now. JournalEntryPayload.category says a category's ID is its nominal code.
  • No Swift change. ResourceID generates as a Swift.String typealias, and the ID and UserID parameters as aliases of it, so every caller compiles as before. The generator names the first Components.Parameters.Id, as its idiomatic naming does with an all-caps name; nothing refers to it.

OpenAPI 3.1.0

The spec moves from openapi: 3.0.3 to 3.1.0, so that the field descriptions beside a $ref reach the generated doc comments.

  • Why they were lost: nine payload fields keep their description beside the $ref, such as InvoiceCreatePayload.project's "Must belong to the contact, or the create is a 422". The bundler keeps it. The generator parses a 3.0 spec with OpenAPIKit30, though, whose schema reference holds only whether it is required, so the description was dropped while parsing. 3.0 also says a $ref's siblings are ignored.
  • Under 3.1.0 OpenAPIKit keeps a reference's description, and siblings beside a $ref are valid. All nine now show in the doc comments.
  • Nothing else changes. Apart from those comments, the generated Types.swift and Client.swift are identical to the 3.0.3 output. The spec uses none of the 3.0-only keywords (nullable, boolean exclusiveMinimum, binary or byte formats).
  • Wrapping each $ref in allOf would also have kept the descriptions, but it generates a value1 wrapper struct per field, which breaks every caller.

Tests

  • Nothing on the wire changes. The generated Client.swift is byte-identical to the one generated from main, and the types differ only by names that stand for Swift.String.
  • Integration: on the first commit, the second full run passed all 61 tests in 21 suites. The first run had one failure whose output wasn't kept. It left an Approved estimate behind, so it was in the estimate lifecycle test, and the estimate was deleted.
  • On the 3.1.0 commit, two full runs each had a failure that doesn't come from this change:
    • Timeslip billing, both runs: FreeAgent answered createInvoice with a 500. The suite passes on its own.
    • Self assessment, once: filing_status differed between list and show, while the suite's mark-filed test toggles that same return in parallel.
    • Both left records behind, now deleted. changed-suites selects both suites, so CI may hit them too.
  • 146 CLI, 90 API and 11 tools unit tests pass, and format and lint are clean.

🤖 Generated with Claude Code

dinoconstantinou87 and others added 3 commits October 3, 2026 15:42
Every input that names a record now references one ResourceID schema,
a record's URL or its ID, after probing each one with a bare ID: 20
query filters, the estimate and invoice fields on item create, and 41
payload fields. The 38 inline path id parameters become one shared Id
parameter on ResourceID, described as refusing a URL, and UserId points
at it too. Credit note item id refuses a URL and stays ID-only; journal
entry stock_item and property can't be probed and stay URL-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generator parses a 3.0 spec with OpenAPIKit30, whose schema
reference keeps only whether it is required, so a description beside a
$ref never reached the generated doc comments. Under 3.1.0 OpenAPIKit
keeps it, and a $ref's siblings are valid. The nine ResourceID field
descriptions now show in the doc comments; the generated code is
otherwise unchanged, and the spec uses no 3.0-only keyword.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Matches ResourceID: the shared path parameter is ID (ID.yaml) and the
self assessment one UserID (UserID.yaml), with every reference updated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dinoconstantinou87
dinoconstantinou87 merged commit 1d04b1c into main Oct 3, 2026
4 checks passed
@dinoconstantinou87
dinoconstantinou87 deleted the feat/resource-id branch October 3, 2026 15:31
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