anyOf not handled correctly
WickyNilliams opened this issue · comments
hey, i think i have come across an issue with the zod schema generated for anyOf
.
consider the example given in the swagger docs: https://swagger.io/docs/specification/data-models/oneof-anyof-allof-not/#anyof
it lists a number of valid and invalid requests. the last of the valid requests is like this:
{
"nickname": "Fido",
"pet_type": "Dog",
"age": 4
}
with the given openapi schema, the generated zod schema looks like this:
const patchPets_Body = z.union([
z.union([PetByAge, PetByType]),
z.array(z.union([PetByAge, PetByType])),
]);
but when parsed, the pet_type
key gets stripped.
looking at the swagger docs, i don't think array should be part of this. in this simple example, might this be correct?
const patchPets_BodyFixed = z.union([
z.intersection(PetByAge, PetByType), // it seems important to zod that this comes first?
PetByAge,
PetByType,
]);
effectively, (A & B) | A | B
(proposed) instead of A | B | Array<A | B>
(current)
i'm not sure if this gets more complicated when there are more than two anyOf
options?
Here's a link to the playground
And here's a stackblitz where i've copied the types from the playground and also applied my possible fix. you can see the fix works as described in the swagger docs, whereas the current output does not https://stackblitz.com/edit/typescript-peyk8j?file=index.ts
hey, feel free to open a PR about this 🙏
to have the same output as you described, you could replace those lines
openapi-zod-client/lib/src/openApiToZod.ts
Lines 111 to 112 in d3c2304
- const oneOf = `z.union([${types}])`;
- return code.assign(`z.union([${oneOf}, z.array(${oneOf})])`);
+ return code.assign(`z.union([z.intersection(${types}), z.array(z.union([${types}]))])`);
and then I'll let you handle the cases where there are more than 2 items
also, please make a dedicated test case / check that the others are still fine and add a changeset
having looked into this a bit more, i think the behavior of anyOf is a little more subtle than i first thought. i've dug up a load of test cases from json-schema (from which openapi inherits anyOf). i'll go through them next week and try to nail down the behavior. i'm actually not 100% sure the types are representable in TS or zod. but i will report back, and land a PR if possible 👍
i'm having an issue fetching the samples for tests.
when i run pnpm gen
as per the readme, i get:
❯ pnpm gen
ERR_PNPM_RECURSIVE_EXEC_FIRST_FAIL Command failed with ENOENT: gen
spawn gen ENOENT
Command "gen" not found.
i'm not familiar with pnpm, seems i can do wildcard commands with regex, so trying that:
❯ pnpm run /gen/
> openapi-zod-client-monorepo@1.4.18 gen:samples /path/to/openapi-zod-client
> rm -rf ./samples && tsx ./lib/samples-generator.ts
(node:18485) ExperimentalWarning: Custom ESM Loaders is an experimental feature and might change at any time
(Use `node --trace-warnings ...` to show where the warning was created)
/path/to/openapi-zod-client/node_modules/.pnpm/degit@2.8.4/node_modules/degit/dist/index-688c5d50.js:14258
throw new DegitError(`could not find commit hash for ${repo.ref}`, {
^
DegitError: could not find commit hash for HEAD
any idea what the issue might be?
hmm weird! maybe something to do with node version?
in any case, i just restored those files in git and decided not to generate afresh, sidestepping that problem.
will make a PR shortly