fix: fail closed on lossy Repository schemas
This commit is contained in:
@@ -13,7 +13,7 @@ cargo run -q -p server-api --example generate_repository_openapi_types
|
||||
cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||
```
|
||||
|
||||
The generator fails closed on unsupported or lossy OpenAPI constructs, including ambiguous nullable unions, external references, unsupported enum literals, and integer ranges that cannot be represented safely by JavaScript numbers. The legacy TypeScript generator imports the two shared Repository source projection types it still needs and no longer declares the migrated Repository list/detail types.
|
||||
The generator fails closed on unsupported or lossy OpenAPI constructs, including ambiguous nullable unions, external references, semantic `$ref` siblings, unimplemented ordinary schema keywords, unsupported enum literals/formats, and integer ranges that cannot be represented safely by JavaScript numbers. Its one explicit format mapping is canonical `uint64` to a nonnegative JavaScript safe `number`; the runtime parser enforces the same range. The legacy TypeScript generator imports the two shared Repository source projection types it still needs and no longer declares the migrated Repository list/detail types.
|
||||
|
||||
## Other generated contracts
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @generated by yoi-repository-openapi-typescript@1. Do not edit.
|
||||
// Input: openapi/server-api.json
|
||||
// Input contract: 0.1.0 (sha256:bac8ba9340cf541183e96715dd4ccd8367f965cdcfc398e6d5091f26a903e1ef)
|
||||
// Options: repository-operation-schema-closure,openapi-3.1,strict-safe-integers,sorted-output
|
||||
// Options: repository-operation-schema-closure,openapi-3.1,format:uint64=safe-number,strict-safe-integers,sorted-output
|
||||
// Output: web/workspace/src/lib/generated/repository-api.ts
|
||||
// Regenerate/check: cargo run -q -p server-api --example generate_repository_openapi_types -- --check
|
||||
|
||||
|
||||
@@ -235,8 +235,8 @@ function gitSummary(value: unknown, path: string): GitRepositorySummary {
|
||||
exactKeys(git, ["status", "head", "branch", "dirty", "remotes"], path);
|
||||
return {
|
||||
status: repositoryString(git.status, `${path}.status`),
|
||||
head: nullableRepositoryString(git.head, `${path}.head`),
|
||||
branch: nullableRepositoryString(git.branch, `${path}.branch`),
|
||||
head: optionalNullableRepositoryString(git.head, `${path}.head`),
|
||||
branch: optionalNullableRepositoryString(git.branch, `${path}.branch`),
|
||||
dirty: boolean(git.dirty, `${path}.dirty`),
|
||||
remotes: repositoryArray(git.remotes, `${path}.remotes`).map((
|
||||
item,
|
||||
|
||||
@@ -50,6 +50,40 @@ Deno.test("generated repository wrapper validates current Backend JSON", () => {
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("repository git optional nullable fields accept omission and null", () => {
|
||||
const omitted = structuredClone(repositoryList) as Record<string, unknown>;
|
||||
const omittedItems = omitted.items as Array<Record<string, unknown>>;
|
||||
omittedItems[0].git = {
|
||||
status: "available",
|
||||
dirty: false,
|
||||
remotes: [],
|
||||
};
|
||||
const omittedParsed = parseRepositoryListResponse(omitted);
|
||||
if (
|
||||
omittedParsed.items[0]?.git?.head !== undefined ||
|
||||
omittedParsed.items[0]?.git?.branch !== undefined
|
||||
) {
|
||||
throw new Error("omitted git fields were not preserved");
|
||||
}
|
||||
|
||||
const nullable = structuredClone(repositoryList) as Record<string, unknown>;
|
||||
const nullableItems = nullable.items as Array<Record<string, unknown>>;
|
||||
nullableItems[0].git = {
|
||||
status: "available",
|
||||
head: null,
|
||||
branch: null,
|
||||
dirty: false,
|
||||
remotes: [],
|
||||
};
|
||||
const nullableParsed = parseRepositoryListResponse(nullable);
|
||||
if (
|
||||
nullableParsed.items[0]?.git?.head !== null ||
|
||||
nullableParsed.items[0]?.git?.branch !== null
|
||||
) {
|
||||
throw new Error("null git fields were not preserved");
|
||||
}
|
||||
});
|
||||
|
||||
Deno.test("plain HTTP repository source kind fails closed at the JSON boundary", () => {
|
||||
const stale = structuredClone(repositoryList) as Record<string, unknown>;
|
||||
const items = stale.items as Array<Record<string, unknown>>;
|
||||
|
||||
Reference in New Issue
Block a user