openapi-ts icon indicating copy to clipboard operation
openapi-ts copied to clipboard

Properly handle readOnly and writeOnly properties

Open tajnymag opened this issue 1 year ago • 10 comments

Describe the solution you'd like As described at original issue and pull request openapi-typescript-codegen does not handle readOnly and writeOnly properties of openapi schemas.

With these properties, it is easier to model properties not for modifiable models.

OpenAPI example:

type: object
properties:
  id:
    # Returned by GET, not used in POST/PUT/PATCH
    type: integer
    readOnly: true
  username:
    type: string
  password:
    # Used in POST/PUT/PATCH, not returned by GET
    type: string
    writeOnly: true

Expected example types (not necessary to be generated separately, as @tiholic suggested in his PR):

UserGetResponse: { id: number, username?: string }
UserPostRequest: { id: number, username?, password?: string }
UserPutRequest: { id: number, username?, password?: string }
UserPatchRequest: { id: number, username?, password?: string }

tajnymag avatar Mar 02 '24 12:03 tajnymag

@tajnymag just want to clarify the expected example types. Aren't they meant to be like this?

UserGetResponse: { id?: number, username?: string }
UserPostRequest: { username?: string, password?: string }
UserPutRequest: { username?: string, password?: string }
UserPatchRequest: { username?: string, password?: string }

mrlubos avatar Mar 03 '24 03:03 mrlubos

@tajnymag just want to clarify the expected example types. Aren't they meant to be like this?

UserGetResponse: { id?: number, username?: string }
UserPostRequest: { username?: string, password?: string }
UserPutRequest: { username?: string, password?: string }
UserPatchRequest: { username?: string, password?: string }

Yeah, they should. You are right.

tajnymag avatar Mar 03 '24 08:03 tajnymag

@tajnymag okay let me think about this. I pushed some changes yesterday that extract types for data objects when using --useOptions, more cool stuff to follow.

P.S. I believe --useOptions should be the default setting, and plan to support mostly that flag in the future

mrlubos avatar Mar 03 '24 14:03 mrlubos

@tajnymag a little update: this will work only with --useOptions, so please make sure you're using that flag. Out of interest, are you able to share your config?

I explored this a bit and the conclusion is that we will need to rework types quite a bit, it's not a quick fix. That pull request you linked doesn't implement it properly either and I'd rather wait until it works as expected than go through multiple iterations here

mrlubos avatar Mar 19 '24 12:03 mrlubos

@mrlubos Unfortunately I don't have anything to share. As I really needed multipart forms supported I've used the official openapi generator. Similarly, as the readOnly and writeOnly properties weren't well supported by any generator I had found, I manually created schemas for requests and responses.

tajnymag avatar Mar 19 '24 14:03 tajnymag

@tajnymag are you not currently using this package then?

mrlubos avatar Mar 19 '24 14:03 mrlubos

@tajnymag are you not currently using this package then?

Unfortunately I am not. I would like to though. The official generators are lacking in so many other ways.

tajnymag avatar Mar 19 '24 15:03 tajnymag

Why don't you use https://github.com/drwpow/openapi-typescript @tajnymag?

mrlubos avatar Mar 19 '24 15:03 mrlubos

Why don't you use https://github.com/drwpow/openapi-typescript @tajnymag?

I didn't want to hardcode any urls into the client. Also I subjectively dislike the API of drwpow/openapi-typescript

tajnymag avatar Mar 19 '24 15:03 tajnymag

Unfortunately I am not. I would like to though. The official generators are lacking in so many other ways.

Don't worry, we'll get there. Keep listing any issues you're seeing elsewhere and we'll figure it out!

mrlubos avatar Mar 19 '24 15:03 mrlubos

Hi @mrlubos

That pull request you linked doesn't implement it properly either and I'd rather wait until it works as expected than go through multiple iterations here

I'm the author of the pull request @tajnymag mentioned. I am planning on migrating to this package from openapi-typescript-codegen to keep things simple moving forward.

When I originally worked on this PR, my primary focus was ensuring the following use case: 1. A schema (e.g., User -> id, name) has readOnly properties (User.id) used in the OpenAPI spec. 2. When this schema is used for POST/PUT service payloads, OmitReadOnly excludes the readOnly properties from the requestBody to efficiently allow re-use of a singe schema for both Read/Write APIs.

I acknowledge there is one particular case I didn’t account for - handling deeply nested schemas with readOnly properties. For example, this schema wouldn't work with that implementation:

Category -> (id [readonly],  name, SubCategory -> (id [readonly], name))

tiholic avatar Dec 26 '24 13:12 tiholic

Hi @tiholic

I bump into the same problem: I have a REST API with loads of readonly properties. Whenever I try to POST something, I need to pass dummy data for these readonly properties to make TS happy. Not nice.

You wrote that your patch doesn't work for deeply nested readonly. Though this patch is not perfect, it would at least handles most cases. So, can you make a pull request with your changes?

Thank you!

topical avatar Jan 20 '25 15:01 topical

Hey all, since TypeScript doesn't support write-only fields, did anyone have any ideas how to generate types that would feel idiomatic while achieving this feature? I'm pretty sure we'll need to split them somehow but didn't think what a good name could be since your spec would refer to a singular Foo schema.

mrlubos avatar Mar 24 '25 15:03 mrlubos

We can generate two types with suffixes, Read and Write, as an option.

yethee avatar Mar 24 '25 15:03 yethee

I use these type names all over the place, so adding a suffix implies a massive refactoring. Sure, there a tools for that but still you get a huge commit and your „blame“ history gets kind of useless.

So, if you really need to update name, then leave the READ version alone and modify the WRITE type name.

topical avatar Mar 25 '25 08:03 topical

@topical do you use write only properties? I'm thinking if only one is used, the name wouldn't change. If both are used, they'd be split into Readable and Writeable. You could override these suffixes to potentially not append one, but it wouldn't be the default behaviour.

mrlubos avatar Mar 25 '25 15:03 mrlubos

Just checked the API: there few writeOnly instances only.

So you propose 3 cases: standard, with readOnly, with writeOnly? Is there any reason why we don’t need 2 cases only and thus omit one of the suffixes?

topical avatar Mar 25 '25 22:03 topical

No, 2 cases: payload and response schema. That's what it is really, and maps to writeable and readable. Omitting suffix by default doesn't allow you to discern between them. You'd never know what Foo and FooReadable are, but FooWriteable and FooReadable sounds more reasonable. As I said though, you could configure it to Foo and FooReadable, but it wouldn't be recommended/default; Foo not mapping literally to Foo in the spec is going to be very confusing. I'd also consider the third case (keep Foo as it is today + writable + readable) but only if it's requested since it won't be used anywhere once we split it into those 2 types.

mrlubos avatar Mar 25 '25 23:03 mrlubos

No, 2 cases: payload and response schema. That's what it is really, and maps to writeable and readable. Omitting suffix by default doesn't allow you to discern between them. You'd never know what Foo and FooReadable are, but FooWriteable and FooReadable sounds more reasonable. As I said though, you could configure it to Foo and FooReadable, but it wouldn't be recommended/default; Foo not mapping literally to Foo in the spec is going to be very confusing. I'd also consider the third case (keep Foo as it is today + writable + readable) but only if it's requested since it won't be used anywhere once we split it into those 2 types.

@mrlubos I think this is a good solution!

dan-cooke avatar Mar 29 '25 11:03 dan-cooke

Let's do it @dan-cooke 🚀 you've no idea how good it feels to finally have a plan on this one, a huge milestone for v1. Sometimes you just need to sit on the problem for a year

mrlubos avatar Mar 29 '25 11:03 mrlubos

@mrlubos oh trust me, I have somewhat of an idea 😄 - have been waiting patiently for this one myself for quite sometime! Thanks for all your hard work

dan-cooke avatar Mar 29 '25 12:03 dan-cooke

Initial diff in the linked pull request looks promising! 🏃

mrlubos avatar Apr 01 '25 08:04 mrlubos

This will be available in v0.66.0. Let me know if you run into any issues! I'm aware of one bug so far, will handle it probably when it gets reported

mrlubos avatar Apr 01 '25 17:04 mrlubos

Hm, now it seems to have broken some things. Since i upgraded to 0.66.4 (from 0.65), I get errors in the types. It refers to *Readable types which are not generated. Is there a way to have it behave like before?

frodehansen2 avatar Apr 14 '25 07:04 frodehansen2

@frodehansen2 https://heyapi.dev/openapi-ts/migrating#read-only-and-write-only-fields

But can you share with me how to reproduce your issue? I'd like to fix that

mrlubos avatar Apr 14 '25 09:04 mrlubos

Hi @mrlubos

I have stripped our spec down for examplem and I also attach the config and the generated types (renamed to .txt for upload).

As you can see in the types.gen, it refers to a ChildSchemaReadable type which is not defined.

types.gen.txt

debug-spec.json

openapi-ts.config.txt

A feature request is maybe to have config to ignore the writeOnly and readOnly?

frodehansen2 avatar Apr 15 '25 11:04 frodehansen2

Thanks @frodehansen2, does the config I sent you not work?

mrlubos avatar Apr 15 '25 11:04 mrlubos

@mrlubos Sorry, I somehow missed that one, my bad. Yes, it works with the readOnlyWriteOnlyBehavior set to "off". Thanks!

frodehansen2 avatar Apr 15 '25 12:04 frodehansen2

@frodehansen2 the issue is btw that all fields are of the same type, I didn't handle that. Will fix

mrlubos avatar Apr 15 '25 12:04 mrlubos

@frodehansen2 the issue you identified will be fixed in v0.66.7. Assuming it builds for you I'd still recommend enabling readOnlyWriteOnlyBehavior 🙌

mrlubos avatar Apr 27 '25 17:04 mrlubos